forked from vieter-v/vieter
Compare commits
390 Commits
Author | SHA1 | Date |
---|---|---|
Jef Roosens | 1a992806fa | |
Jef Roosens | 22577d3411 | |
Jef Roosens | 47c0f0405b | |
Jef Roosens | 076ee24b1b | |
Jef Roosens | de8764b281 | |
Jef Roosens | b278ebd73f | |
Jef Roosens | afb38256ac | |
Jef Roosens | ac3a89500b | |
Jef Roosens | 8a76860363 | |
Jef Roosens | 7595eb7bbe | |
Jef Roosens | 37f368b769 | |
Jef Roosens | 094634084b | |
Jef Roosens | 3b24ad0f2c | |
Jef Roosens | 69cc2404db | |
Jef Roosens | f423dcf26b | |
Jef Roosens | beae2cebd2 | |
Jef Roosens | a3a83a94ae | |
Jef Roosens | 455f3b5f41 | |
Jef Roosens | 4dc82515f4 | |
Jef Roosens | bff817ccd9 | |
Jef Roosens | 8a08423907 | |
Jef Roosens | b9598ca046 | |
Jef Roosens | 91a976c634 | |
Jef Roosens | b3a119f221 | |
Jef Roosens | e10b450abd | |
Jef Roosens | 8f32888dff | |
Jef Roosens | da370f42fd | |
Jef Roosens | 0d6ca8d3e4 | |
Jef Roosens | 434c4eb558 | |
Jef Roosens | 3b320ac7c3 | |
Jef Roosens | 8d14d5c3fd | |
Jef Roosens | 6ca53ce534 | |
Jef Roosens | ba89110eab | |
Jef Roosens | ad19bc660a | |
Jef Roosens | beb90d5756 | |
Jef Roosens | bfd28d6f70 | |
GreekStapler | 8432f5915d | |
Jef Roosens | 5176266ca1 | |
Jef Roosens | b5ff50066b | |
Jef Roosens | 398e2bd9eb | |
Jef Roosens | 39a026fdb3 | |
Jef Roosens | b0fe6b7384 | |
Jef Roosens | c9edb55abc | |
Jef Roosens | f8f611f5c5 | |
Jef Roosens | 60d5fb77e0 | |
Jef Roosens | 849bf54979 | |
Jef Roosens | 4ed4ef4a27 | |
Jef Roosens | 4ca2521937 | |
Jef Roosens | c0f58ddc77 | |
Jef Roosens | 8a0214babe | |
Jef Roosens | 1c70bce9e4 | |
Jef Roosens | 6738f8de67 | |
Jef Roosens | 4635127ba2 | |
Jef Roosens | bb4406404d | |
Jef Roosens | cac74db086 | |
Jef Roosens | b7af051103 | |
Jef Roosens | 641cf22669 | |
Jef Roosens | 3342eedfa4 | |
Jef Roosens | dc517c23c5 | |
Jef Roosens | be3762835d | |
Jef Roosens | ab6da78738 | |
Jef Roosens | 2c93316688 | |
Jef Roosens | 0a3e883f4d | |
Jef Roosens | ab81eebd87 | |
Jef Roosens | b66d1161ed | |
Jef Roosens | 26796f2228 | |
Jef Roosens | 09c61143b0 | |
Jef Roosens | a9ad3088bb | |
Jef Roosens | af409011e6 | |
Jef Roosens | 8b72a9fc0f | |
Jef Roosens | f9bb4b81de | |
Jef Roosens | 300c5490a6 | |
Jef Roosens | b067f9c589 | |
Jef Roosens | 1797c0f560 | |
Jef Roosens | 946d9acd59 | |
Jef Roosens | 402fef475a | |
Jef Roosens | 894323ddcb | |
Jef Roosens | fe3e6e2bab | |
Jef Roosens | af4c9e1d00 | |
Jef Roosens | 0604de26c4 | |
Jef Roosens | 489931eaa8 | |
Jef Roosens | 1ce7b9d571 | |
Jef Roosens | a48358fd75 | |
Jef Roosens | dbbe5c1e51 | |
Jef Roosens | b634775ca3 | |
Jef Roosens | 0727d0fd25 | |
Jef Roosens | 0bd5158608 | |
Jef Roosens | bfe1aafcf1 | |
Jef Roosens | 60cb91c18c | |
Jef Roosens | 51df1874f5 | |
Jef Roosens | d7a04c6ebf | |
Jef Roosens | 2cc3e8404e | |
Jef Roosens | 6a208dbe6c | |
Jef Roosens | f6c5e7c246 | |
Jef Roosens | 8a2f720bdf | |
Jef Roosens | d3151863ee | |
Jef Roosens | 03f2240ff6 | |
Jef Roosens | 5cbfc0ebcb | |
Jef Roosens | 6342789921 | |
Jef Roosens | e742d3de6d | |
Jef Roosens | b6168a3060 | |
Jef Roosens | 882a9a60a9 | |
Jef Roosens | 3611123f45 | |
Jef Roosens | 6f23d690a7 | |
Jef Roosens | 7ef8d4b846 | |
Jef Roosens | 5bab1f77f0 | |
Jef Roosens | 0a5c4295e0 | |
Jef Roosens | c57de4d8ee | |
Jef Roosens | 9a49d96e20 | |
Jef Roosens | 6281ef7607 | |
Jef Roosens | 9e11237ff9 | |
Jef Roosens | 71c77e90bc | |
Jef Roosens | 54f40b7638 | |
Jef Roosens | 9493796160 | |
Jef Roosens | 3636dd92db | |
Jef Roosens | 17e58c91ed | |
Jef Roosens | fc4dc30f74 | |
Jef Roosens | 5542be0418 | |
Jef Roosens | 9a552f5302 | |
Jef Roosens | aff6dff06a | |
Jef Roosens | 3095daed7d | |
Jef Roosens | cc9dcb3058 | |
Jef Roosens | 96a9798d3f | |
Jef Roosens | 161341a108 | |
Jef Roosens | a2fda0d4b7 | |
Jef Roosens | 23632be7a4 | |
Jef Roosens | 22fd6e395b | |
Jef Roosens | ed29102717 | |
Jef Roosens | ae29fe5ef8 | |
Jef Roosens | 66928216e5 | |
Jef Roosens | 7ef7dcd725 | |
Jef Roosens | e9fdbc9426 | |
Jef Roosens | 15c2d72743 | |
Jef Roosens | f34eefd59b | |
Jef Roosens | ae98c3e717 | |
Jef Roosens | 559ef3e505 | |
Jef Roosens | 847d77b2bc | |
Jef Roosens | 95d32e2d51 | |
Jef Roosens | 575c04189d | |
Jef Roosens | 39e2d12827 | |
Jef Roosens | 851a446a95 | |
Jef Roosens | fab8ca20b8 | |
Jef Roosens | 8a08788935 | |
Jef Roosens | cf67b46df0 | |
Jef Roosens | b6cd2f0bc2 | |
Jef Roosens | 210508f1ee | |
Jef Roosens | 7b59277931 | |
Jef Roosens | 3e0a2584fa | |
Jef Roosens | 9dfdfbf724 | |
Jef Roosens | 272f14b264 | |
Jef Roosens | 4887af26d3 | |
Jef Roosens | 9268ef0302 | |
Jef Roosens | e23635a1d3 | |
Jef Roosens | cc5df95a1a | |
Jef Roosens | e7b45bf251 | |
Jef Roosens | 3a73ea0632 | |
Jef Roosens | 78b0918df7 | |
Jef Roosens | 50918da672 | |
Jef Roosens | ba3b00572b | |
Jef Roosens | 68b7e5e71e | |
Jef Roosens | 6283cbea9c | |
Jef Roosens | 49ddb312de | |
Jef Roosens | 8a2b121cc7 | |
Jef Roosens | 0f6630b940 | |
Jef Roosens | 0d0fb323f2 | |
Jef Roosens | 1a940f2f98 | |
Jef Roosens | 487b235727 | |
Jef Roosens | 1b7cabdd74 | |
Jef Roosens | 6336d801d3 | |
Jef Roosens | 25d87fb5e6 | |
Jef Roosens | af4fbc4ccc | |
Jef Roosens | c8fc4c6a96 | |
Jef Roosens | 9dd9222a69 | |
Jef Roosens | a4c2508fe7 | |
Jef Roosens | e58ac49680 | |
Jef Roosens | 461f227169 | |
Jef Roosens | d060366dcb | |
Jef Roosens | 39eb03077e | |
Jef Roosens | 1ac76ac452 | |
Jef Roosens | 4200f5c8de | |
Jef Roosens | 424b0651e9 | |
Jef Roosens | 5e11a91f3d | |
Jef Roosens | 449656eb97 | |
Jef Roosens | 8f91c1fde5 | |
Jef Roosens | bd07964509 | |
Jef Roosens | bb5643bb03 | |
Jef Roosens | 10ad8297fb | |
Jef Roosens | a8d647cca3 | |
Jef Roosens | 1b7c14e7dc | |
Jef Roosens | f81039d2bb | |
Jef Roosens | 3d38df6d03 | |
Jef Roosens | cf94b64400 | |
Jef Roosens | fcdcf9c5ca | |
Jef Roosens | 9727b86203 | |
Jef Roosens | 102a7f8899 | |
Jef Roosens | faec08f846 | |
Jef Roosens | 4d581da7bf | |
Jef Roosens | 6b79f7b5ed | |
Jef Roosens | 3a5ac5d32b | |
Jef Roosens | 339267e6b2 | |
Jef Roosens | 233dd20345 | |
Jef Roosens | ae04fe63a7 | |
Jef Roosens | 592241c743 | |
Jef Roosens | 44696fc11b | |
Jef Roosens | 4866cfa635 | |
Jef Roosens | 12805d713c | |
Jef Roosens | 03df62bbc4 | |
Jef Roosens | ec128539d2 | |
Jef Roosens | 9dc8db4d54 | |
Jef Roosens | 0855d9efd8 | |
Jef Roosens | f4b2109533 | |
Jef Roosens | 440d1753da | |
Jef Roosens | ddccceb336 | |
Jef Roosens | c15f4a482f | |
Jef Roosens | c341d7a024 | |
Jef Roosens | a9ddfd8ec8 | |
Jef Roosens | 0ab39a334d | |
Jef Roosens | d7d77afe09 | |
Jef Roosens | 4ecf6a11c4 | |
Jef Roosens | 5df5850044 | |
Jef Roosens | 95441bdea0 | |
Jef Roosens | aea83c38ef | |
Jef Roosens | fc6d3909d2 | |
Jef Roosens | e734e658a0 | |
Jef Roosens | 7ad5830e9f | |
Jef Roosens | 7c2f892162 | |
Jef Roosens | 4870edde51 | |
Jef Roosens | a02b70e9a5 | |
Jef Roosens | 1d034e670e | |
Jef Roosens | 2abbc11118 | |
Jef Roosens | 0e8e7223ab | |
Jef Roosens | f9a6d585dd | |
Jef Roosens | ae50e29597 | |
Jef Roosens | e5582a2d54 | |
Jef Roosens | 329e819e15 | |
Jef Roosens | 06df2c21f0 | |
Jef Roosens | 48e2ae7645 | |
Jef Roosens | 9f753f9c93 | |
Jef Roosens | ec92b16a73 | |
Jef Roosens | aded6d438a | |
Jef Roosens | edd71b41c2 | |
Jef Roosens | f0565c4168 | |
Jef Roosens | a4ffc2c0e3 | |
Jef Roosens | 401e0291e3 | |
Jef Roosens | a39c1aa5eb | |
Jef Roosens | 4f32dec5b5 | |
Jef Roosens | 31e903ebeb | |
Jef Roosens | 596da100b6 | |
Jef Roosens | 0d5704ba15 | |
Jef Roosens | cdb88e1620 | |
Jef Roosens | 768da5b790 | |
Jef Roosens | bd4bb9a9fb | |
Jef Roosens | c0b739035b | |
Jef Roosens | 7f6e9e636c | |
Jef Roosens | 96d0c2f1eb | |
Jef Roosens | 0233b8559d | |
Jef Roosens | 2fc25f1afe | |
Jef Roosens | 6bd5b7cb48 | |
Jef Roosens | 5e81dadce3 | |
Jef Roosens | 1e079143cd | |
Jef Roosens | 7627b28bcf | |
Jef Roosens | 0de5ffb45d | |
Jef Roosens | 29e6d8d071 | |
Jef Roosens | 67c4d19921 | |
Jef Roosens | 73d2d4b08f | |
Jef Roosens | 92f73ad364 | |
Jef Roosens | 889d5a0884 | |
Jef Roosens | 3c87e60293 | |
Jef Roosens | 850cba6ab9 | |
Jef Roosens | d4c803c41c | |
Jef Roosens | 055b168ff1 | |
Jef Roosens | 1d3c7a1651 | |
Jef Roosens | 97cdaa18e1 | |
Jef Roosens | ce67208fbd | |
Jef Roosens | e041682fea | |
Jef Roosens | 79fd9c1f87 | |
Jef Roosens | 1811ebbe3f | |
Jef Roosens | f22ed29631 | |
Jef Roosens | 92cbea69d6 | |
Jef Roosens | da46b8b4ae | |
Jef Roosens | 4c97489f8a | |
Jef Roosens | dd9958ea28 | |
Jef Roosens | 473c7ec06b | |
Jef Roosens | c01d849b9e | |
Jef Roosens | 5c5c2f87e0 | |
Jef Roosens | b91d7b3159 | |
Jef Roosens | 5f21e256ee | |
Jef Roosens | 53f5b68d08 | |
Jef Roosens | 06bab98a88 | |
Jef Roosens | ad207bdb70 | |
Jef Roosens | c018aad143 | |
LordMZTE | 0bac221aee | |
Jef Roosens | 78fc3afcd3 | |
Jef Roosens | cae44fb593 | |
Jef Roosens | 3821ed29fd | |
Jef Roosens | 5a5f7f8346 | |
Jef Roosens | ea4c4fce16 | |
Jef Roosens | e79d18100f | |
Jef Roosens | 4b172cb5d8 | |
Jef Roosens | 27aa215eff | |
Jef Roosens | 7e5f0c5a53 | |
Jef Roosens | 30cce4fa72 | |
Jef Roosens | 5f7d7c4780 | |
Jef Roosens | 5b016df85d | |
Jef Roosens | fa6603bd45 | |
Jef Roosens | 407b226955 | |
Jef Roosens | f42d3fd8b0 | |
Jef Roosens | 139142fcec | |
Jef Roosens | 393e641a76 | |
Jef Roosens | 7e01dbafec | |
Jef Roosens | 58c1ecd25e | |
Jef Roosens | 230920576d | |
Jef Roosens | 356a34ab01 | |
Jef Roosens | 1156e896f7 | |
Jef Roosens | a3b6680153 | |
Jef Roosens | 7fdbcdf3e7 | |
Jef Roosens | d4306133e0 | |
Jef Roosens | 1990ade089 | |
Jef Roosens | e008133981 | |
Jef Roosens | 1a076a7a8c | |
Jef Roosens | 8c5652c230 | |
Jef Roosens | b6d5bd3228 | |
Jef Roosens | 5781796e99 | |
Jef Roosens | 204144cee8 | |
Jef Roosens | c818273790 | |
Jef Roosens | 7419144f97 | |
Jef Roosens | 0a2488a4df | |
Jef Roosens | 891a206116 | |
Jef Roosens | 03318586ed | |
Jef Roosens | c5161cac37 | |
Jef Roosens | 5cde3d0235 | |
Jef Roosens | 0b050a81db | |
Jef Roosens | d313c5b786 | |
Jef Roosens | 92b8f1fb93 | |
Jef Roosens | b1ac39e234 | |
Jef Roosens | d9d7272b44 | |
Jef Roosens | 1f1aa381e1 | |
Jef Roosens | e5d50f3a59 | |
Jef Roosens | 1dd810a605 | |
Jef Roosens | 325dcc27de | |
Jef Roosens | 37c27ae84b | |
Jef Roosens | 60598f719c | |
Jef Roosens | 4a47c7bbdc | |
Jef Roosens | cfacf9ed0f | |
Jef Roosens | f9f440500e | |
Jef Roosens | fb65efdfbe | |
Jef Roosens | 369b4458c5 | |
Jef Roosens | 98c0e52b08 | |
Jef Roosens | caee56efd4 | |
Jef Roosens | a1c308f29d | |
Jef Roosens | 11ac3c0470 | |
Jef Roosens | 5287067ea7 | |
Jef Roosens | 6f9e1b5f3c | |
Jef Roosens | 4d26797453 | |
Jef Roosens | 7722d5a7e4 | |
Jef Roosens | 20707f6af1 | |
Jef Roosens | 92ca5b8024 | |
Jef Roosens | 20112b8693 | |
Jef Roosens | cf77037188 | |
Jef Roosens | cd8fd78616 | |
Jef Roosens | c8fc683384 | |
Jef Roosens | c8af362a4a | |
Jef Roosens | 78b477fb92 | |
Jef Roosens | f7e1aba30b | |
Jef Roosens | ff57d73998 | |
Jef Roosens | 132a7a8ba5 | |
Jef Roosens | 9a56bd03a7 | |
Jef Roosens | 05b34d3ffd | |
Jef Roosens | bd0c276fd8 | |
Jef Roosens | 5ce431aa4a | |
Jef Roosens | 1116fee3fc | |
Jef Roosens | e6033f9ab4 | |
Jef Roosens | eb65bb8a69 | |
Jef Roosens | cb22c1c9d3 | |
Jef Roosens | 5c38071998 | |
Jef Roosens | 65d6aae701 | |
Jef Roosens | 2942793f40 | |
Jef Roosens | f4bb03f488 | |
Jef Roosens | 04e54b8b10 | |
Jef Roosens | ab4f64b6b6 | |
Jef Roosens | 0e5f31e649 | |
Jef Roosens | 135b6c3d7f | |
Jef Roosens | 18a18864e4 | |
Renovate Bot | 4a68cb3c03 | |
Jef Roosens | e3da3d0d7f | |
Jef Roosens | 799fe2e454 | |
Jef Roosens | f92a20fcf8 | |
Jef Roosens | 6d60ea1538 | |
Jef Roosens | e890128bda | |
Jef Roosens | 41ee08045b |
|
@ -0,0 +1,4 @@
|
||||||
|
# To stay consistent with the V formatting style, we use tabs
|
||||||
|
UseTab: Always
|
||||||
|
IndentWidth: 4
|
||||||
|
TabWidth: 4
|
|
@ -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
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
*.c
|
vieter.c
|
||||||
data/
|
/data/
|
||||||
|
|
||||||
# Build artifacts
|
# Build artifacts
|
||||||
vieter
|
/vieter
|
||||||
dvieter
|
/dvieter
|
||||||
pvieter
|
/pvieter
|
||||||
dvieterctl
|
/suvieter
|
||||||
vieterctl
|
/afvieter
|
||||||
vieter.c
|
/vieter.c
|
||||||
|
|
||||||
# Ignore testing files
|
# Ignore testing files
|
||||||
*.pkg*
|
*.pkg*
|
||||||
|
@ -23,3 +23,11 @@ v/
|
||||||
|
|
||||||
# gdb log file
|
# gdb log file
|
||||||
gdb.txt
|
gdb.txt
|
||||||
|
|
||||||
|
# Generated docs
|
||||||
|
_docs/
|
||||||
|
docs/resources/_gen/
|
||||||
|
/man/
|
||||||
|
|
||||||
|
# VLS logs
|
||||||
|
vls.log
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
[submodule "docs/themes/hugo-book"]
|
||||||
|
path = docs/themes/hugo-book
|
||||||
|
url = https://github.com/alex-shpak/hugo-book
|
||||||
|
[submodule "src/libvieter"]
|
||||||
|
path = src/libvieter
|
||||||
|
url = https://git.rustybever.be/vieter-v/libvieter
|
|
@ -1,30 +0,0 @@
|
||||||
branches: [main, dev]
|
|
||||||
platform: linux/amd64
|
|
||||||
depends_on:
|
|
||||||
- build
|
|
||||||
|
|
||||||
pipeline:
|
|
||||||
dev:
|
|
||||||
image: woodpeckerci/plugin-docker-buildx
|
|
||||||
secrets: [ docker_username, docker_password ]
|
|
||||||
settings:
|
|
||||||
repo: chewingbever/vieter
|
|
||||||
tag: dev
|
|
||||||
platforms: [ linux/arm64/v8, linux/amd64 ]
|
|
||||||
build_args_from_env:
|
|
||||||
- CI_COMMIT_SHA
|
|
||||||
when:
|
|
||||||
event: push
|
|
||||||
branch: dev
|
|
||||||
|
|
||||||
release:
|
|
||||||
image: woodpeckerci/plugin-docker-buildx
|
|
||||||
secrets: [ docker_username, docker_password ]
|
|
||||||
settings:
|
|
||||||
repo: chewingbever/vieter
|
|
||||||
auto_tag: true
|
|
||||||
platforms: [ linux/arm64/v8, linux/amd64 ]
|
|
||||||
build_args_from_env:
|
|
||||||
- CI_COMMIT_SHA
|
|
||||||
when:
|
|
||||||
event: tag
|
|
|
@ -1,13 +0,0 @@
|
||||||
# These checks already get performed on the feature branches
|
|
||||||
branches:
|
|
||||||
exclude: [ main, dev ]
|
|
||||||
platform: linux/amd64
|
|
||||||
|
|
||||||
pipeline:
|
|
||||||
lint:
|
|
||||||
image: 'chewingbever/vlang:latest'
|
|
||||||
pull: true
|
|
||||||
group: lint
|
|
||||||
commands:
|
|
||||||
- make lint
|
|
||||||
- make vet
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
matrix:
|
||||||
|
PLATFORM:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
|
|
||||||
|
platform: ${PLATFORM}
|
||||||
|
branches: [main]
|
||||||
|
skip_clone: true
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: 'git.rustybever.be/vieter-v/vieter-builder'
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
# 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
|
||||||
|
# Update packages
|
||||||
|
- pacman -Syu --noconfirm
|
||||||
|
# Create non-root user to perform build & switch to their home
|
||||||
|
- groupadd -g 1000 builder
|
||||||
|
- useradd -mg builder builder
|
||||||
|
- chown -R builder:builder "$PWD"
|
||||||
|
- "echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers"
|
||||||
|
- su builder
|
||||||
|
# Due to a bug with the V compiler, we can't just use the PKGBUILD from
|
||||||
|
# inside the repo
|
||||||
|
- curl -OL "https://git.rustybever.be/vieter-v/vieter/raw/tag/$CI_COMMIT_TAG/PKGBUILD"
|
||||||
|
- makepkg -s --noconfirm --needed
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
|
||||||
|
publish:
|
||||||
|
image: 'curlimages/curl'
|
||||||
|
commands:
|
||||||
|
# Publish the package
|
||||||
|
- 'for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $VIETER_API_KEY" https://arch.r8r.be/vieter/publish; done'
|
||||||
|
secrets:
|
||||||
|
- vieter_api_key
|
||||||
|
when:
|
||||||
|
event: tag
|
|
@ -5,11 +5,15 @@ matrix:
|
||||||
|
|
||||||
platform: ${PLATFORM}
|
platform: ${PLATFORM}
|
||||||
branches: [dev]
|
branches: [dev]
|
||||||
|
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
|
||||||
|
- echo -e '[vieter]\nServer = https://arch.r8r.be/$repo/$arch\nSigLevel = Optional' >> /etc/pacman.conf
|
||||||
# Update packages
|
# Update packages
|
||||||
- pacman -Syu --noconfirm
|
- pacman -Syu --noconfirm
|
||||||
# Create non-root user to perform build & switch to their home
|
# Create non-root user to perform build & switch to their home
|
||||||
|
@ -18,8 +22,12 @@ pipeline:
|
||||||
- chown -R builder:builder "$PWD"
|
- chown -R builder:builder "$PWD"
|
||||||
- "echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers"
|
- "echo 'builder ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers"
|
||||||
- su builder
|
- su builder
|
||||||
# Build the package
|
# Due to a bug with the V compiler, we can't just use the PKGBUILD from
|
||||||
|
# inside the repo
|
||||||
|
- 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:
|
||||||
|
event: push
|
||||||
|
|
||||||
publish:
|
publish:
|
||||||
image: 'curlimages/curl'
|
image: 'curlimages/curl'
|
||||||
|
@ -28,3 +36,5 @@ pipeline:
|
||||||
- 'for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $VIETER_API_KEY" https://arch.r8r.be/vieter/publish; done'
|
- 'for pkg in $(ls -1 *.pkg*); do curl -XPOST -T "$pkg" -H "X-API-KEY: $VIETER_API_KEY" https://arch.r8r.be/vieter/publish; done'
|
||||||
secrets:
|
secrets:
|
||||||
- vieter_api_key
|
- vieter_api_key
|
||||||
|
when:
|
||||||
|
event: push
|
|
@ -1,32 +1,41 @@
|
||||||
|
variables:
|
||||||
|
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
PLATFORM:
|
PLATFORM:
|
||||||
- linux/amd64
|
- 'linux/amd64'
|
||||||
- linux/arm64
|
- 'linux/arm64'
|
||||||
# I just don't have a performant enough runner for this platform
|
|
||||||
# - linux/arm/v7
|
|
||||||
|
|
||||||
# These checks already get performed on the feature branches
|
|
||||||
platform: ${PLATFORM}
|
platform: ${PLATFORM}
|
||||||
|
|
||||||
pipeline:
|
pipeline:
|
||||||
# The default build isn't needed, as alpine switches to gcc for the compiler anyways
|
install-modules:
|
||||||
debug:
|
image: *vlang_image
|
||||||
image: 'chewingbever/vlang:latest'
|
|
||||||
pull: true
|
pull: true
|
||||||
group: 'build'
|
|
||||||
commands:
|
commands:
|
||||||
- make debug
|
- export VMODULES=$PWD/.vmodules
|
||||||
|
- 'cd src && v install'
|
||||||
when:
|
when:
|
||||||
event: push
|
event: [push, pull_request]
|
||||||
|
|
||||||
|
debug:
|
||||||
|
image: *vlang_image
|
||||||
|
commands:
|
||||||
|
- export VMODULES=$PWD/.vmodules
|
||||||
|
- make
|
||||||
|
when:
|
||||||
|
event: [pull_request]
|
||||||
|
branch:
|
||||||
|
exclude: [main]
|
||||||
|
|
||||||
prod:
|
prod:
|
||||||
image: 'chewingbever/vlang:latest'
|
image: *vlang_image
|
||||||
pull: true
|
|
||||||
environment:
|
environment:
|
||||||
- LDFLAGS=-lz -lbz2 -llzma -lexpat -lzstd -llz4 -static
|
- LDFLAGS=-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static
|
||||||
group: 'build'
|
|
||||||
commands:
|
commands:
|
||||||
- make prod
|
- export VMODULES=$PWD/.vmodules
|
||||||
|
# Apparently this -D is *very* important
|
||||||
|
- CFLAGS='-DGC_THREADS=1' make prod
|
||||||
# Make sure the binary is actually statically built
|
# Make sure the binary is actually statically built
|
||||||
- readelf -d pvieter
|
- readelf -d pvieter
|
||||||
- du -h pvieter
|
- du -h pvieter
|
||||||
|
@ -35,10 +44,10 @@ pipeline:
|
||||||
- strip -s pvieter
|
- strip -s pvieter
|
||||||
- du -h pvieter
|
- du -h pvieter
|
||||||
when:
|
when:
|
||||||
event: push
|
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
|
||||||
|
@ -48,10 +57,11 @@ 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
|
||||||
|
--fail
|
||||||
-XPUT
|
-XPUT
|
||||||
-T pvieter
|
-T pvieter
|
||||||
-H "Host: $URL"
|
-H "Host: $URL"
|
||||||
|
@ -60,4 +70,4 @@ pipeline:
|
||||||
-H "Authorization: AWS $S3_USERNAME:$SIGNATURE"
|
-H "Authorization: AWS $S3_USERNAME:$SIGNATURE"
|
||||||
https://$URL$OBJ_PATH
|
https://$URL$OBJ_PATH
|
||||||
when:
|
when:
|
||||||
event: push
|
event: [push, pull_request]
|
|
@ -0,0 +1,18 @@
|
||||||
|
branches: [ 'dev' ]
|
||||||
|
platform: 'linux/amd64'
|
||||||
|
depends_on:
|
||||||
|
- 'docker'
|
||||||
|
|
||||||
|
skip_clone: true
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
webhooks:
|
||||||
|
image: 'curlimages/curl'
|
||||||
|
secrets:
|
||||||
|
- 'webhook_app'
|
||||||
|
- 'webhook_cron'
|
||||||
|
commands:
|
||||||
|
- 'curl -XPOST -s --fail $WEBHOOK_APP'
|
||||||
|
- 'curl -XPOST -s --fail $WEBHOOK_CRON'
|
||||||
|
when:
|
||||||
|
event: push
|
|
@ -0,0 +1,36 @@
|
||||||
|
branches: [main, dev]
|
||||||
|
platform: 'linux/amd64'
|
||||||
|
depends_on:
|
||||||
|
- build
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
dev:
|
||||||
|
image: 'woodpeckerci/plugin-docker-buildx'
|
||||||
|
secrets:
|
||||||
|
- 'docker_username'
|
||||||
|
- 'docker_password'
|
||||||
|
settings:
|
||||||
|
repo: 'chewingbever/vieter'
|
||||||
|
tags:
|
||||||
|
- 'dev'
|
||||||
|
- ${CI_COMMIT_SHA}
|
||||||
|
platforms: [ 'linux/arm64/v8', 'linux/amd64' ]
|
||||||
|
build_args_from_env:
|
||||||
|
- 'CI_COMMIT_SHA'
|
||||||
|
when:
|
||||||
|
event: push
|
||||||
|
branch: dev
|
||||||
|
|
||||||
|
release:
|
||||||
|
image: 'woodpeckerci/plugin-docker-buildx'
|
||||||
|
secrets:
|
||||||
|
- 'docker_username'
|
||||||
|
- 'docker_password'
|
||||||
|
settings:
|
||||||
|
repo: 'chewingbever/vieter'
|
||||||
|
auto_tag: true
|
||||||
|
platforms: [ 'linux/arm64/v8', 'linux/amd64' ]
|
||||||
|
build_args_from_env:
|
||||||
|
- 'CI_COMMIT_SHA'
|
||||||
|
when:
|
||||||
|
event: tag
|
|
@ -0,0 +1,50 @@
|
||||||
|
variables:
|
||||||
|
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
|
||||||
|
|
||||||
|
platform: 'linux/amd64'
|
||||||
|
branches:
|
||||||
|
exclude: [ main ]
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
docs:
|
||||||
|
image: 'klakegg/hugo:ext-alpine'
|
||||||
|
group: 'generate'
|
||||||
|
commands:
|
||||||
|
- apk add git
|
||||||
|
- make docs
|
||||||
|
|
||||||
|
api-docs:
|
||||||
|
image: *vlang_image
|
||||||
|
pull: true
|
||||||
|
group: 'generate'
|
||||||
|
commands:
|
||||||
|
- make api-docs
|
||||||
|
|
||||||
|
slate-docs:
|
||||||
|
image: 'slatedocs/slate:v2.13.0'
|
||||||
|
group: 'generate'
|
||||||
|
# Slate requires a specific directory to run in
|
||||||
|
commands:
|
||||||
|
- cd docs/api
|
||||||
|
- bundle exec middleman build --clean
|
||||||
|
|
||||||
|
archive:
|
||||||
|
image: 'alpine'
|
||||||
|
commands:
|
||||||
|
- cp -r docs/api/build docs/public/api
|
||||||
|
- 'cd docs/public && tar czvf ../../docs.tar.gz *'
|
||||||
|
- 'cd ../../src/_docs && tar czvf ../../api-docs.tar.gz *'
|
||||||
|
when:
|
||||||
|
event: push
|
||||||
|
branch: dev
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
image: 'curlimages/curl'
|
||||||
|
secrets:
|
||||||
|
- 'site_api_key'
|
||||||
|
commands:
|
||||||
|
- 'curl -XPOST --fail -s -H "Authorization: Bearer $SITE_API_KEY" -T docs.tar.gz https://rustybever.be/api/deploy?dir=docs-vieter'
|
||||||
|
- 'curl -XPOST --fail -s -H "Authorization: Bearer $SITE_API_KEY" -T api-docs.tar.gz https://rustybever.be/api/deploy?dir=api-docs-vieter'
|
||||||
|
when:
|
||||||
|
event: push
|
||||||
|
branch: dev
|
|
@ -1,6 +1,8 @@
|
||||||
# Yeah so this only works on tags so we'll worry about this later
|
variables:
|
||||||
platform: linux/amd64
|
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
|
||||||
branches: main
|
|
||||||
|
platform: 'linux/amd64'
|
||||||
|
branches: [ 'main' ]
|
||||||
depends_on:
|
depends_on:
|
||||||
- build
|
- build
|
||||||
|
|
||||||
|
@ -9,12 +11,13 @@ 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:
|
||||||
- mc alias set s3/ https://s3.rustybever.be "$S3_USERNAME" "$S3_PASSWORD"
|
- mc alias set s3/ https://s3.rustybever.be "$S3_USERNAME" "$S3_PASSWORD"
|
||||||
- mc cp -r "s3/vieter/commits/$CI_COMMIT_SHA" .
|
- mc cp -r "s3/vieter/commits/$CI_COMMIT_SHA" .
|
||||||
|
- mv "$CI_COMMIT_SHA"/vieter-* .
|
||||||
when:
|
when:
|
||||||
event: tag
|
event: tag
|
||||||
|
|
||||||
|
@ -24,9 +27,8 @@ pipeline:
|
||||||
- gitea_release_api_key
|
- gitea_release_api_key
|
||||||
settings:
|
settings:
|
||||||
base_url: https://git.rustybever.be
|
base_url: https://git.rustybever.be
|
||||||
files: ${CI_COMMIT_SHA}/*
|
files: vieter-*
|
||||||
checksum:
|
checksum:
|
||||||
- md5
|
|
||||||
- sha256
|
- sha256
|
||||||
title: ${CI_COMMIT_TAG}
|
title: ${CI_COMMIT_TAG}
|
||||||
when:
|
when:
|
|
@ -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]
|
|
@ -0,0 +1,45 @@
|
||||||
|
variables:
|
||||||
|
- &vlang_image 'git.rustybever.be/vieter/vlang:5d4c9dc9fc11bf8648541c934adb64f27cb94e37-alpine3.17'
|
||||||
|
|
||||||
|
platform: 'linux/amd64'
|
||||||
|
branches:
|
||||||
|
exclude: [ main ]
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- build
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
install-modules:
|
||||||
|
image: *vlang_image
|
||||||
|
pull: true
|
||||||
|
commands:
|
||||||
|
- export VMODULES=$PWD/.vmodules
|
||||||
|
- '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
|
||||||
|
- cd man
|
||||||
|
|
||||||
|
# Generate an HTML page from each man page
|
||||||
|
- for f in $(ls -1 *.1); do mandoc -Thtml -O style=mandoc.css,man=%N.%S.html $f > "$f.html"; done
|
||||||
|
|
||||||
|
# Download the mandoc.css file from the official site
|
||||||
|
- curl -o mandoc.css -L https://mandoc.bsd.lv/mandoc.css
|
||||||
|
|
||||||
|
- tar czvf ../man.tar.gz *.html mandoc.css
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
image: 'curlimages/curl'
|
||||||
|
secrets:
|
||||||
|
- 'site_api_key'
|
||||||
|
commands:
|
||||||
|
- 'curl -XPOST --fail -s -H "Authorization: Bearer $SITE_API_KEY" -T man.tar.gz https://rustybever.be/api/deploy?dir=man-vieter'
|
||||||
|
when:
|
||||||
|
event: push
|
||||||
|
branch: dev
|
|
@ -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]
|
191
CHANGELOG.md
191
CHANGELOG.md
|
@ -5,9 +5,192 @@ 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/Chewing_Bever/vieter)
|
## [Unreleased](https://git.rustybever.be/vieter-v/vieter/src/branch/dev)
|
||||||
|
|
||||||
## [0.2.0](https://git.rustybever.be/Chewing_Bever/vieter/src/tag/0.2.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.
|
||||||
|
|
||||||
|
## [0.3.0-rc.1](https://git.rustybever.be/vieter-v/vieter/src/tag/0.3.0-rc.1)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Database migrations
|
||||||
|
* Improved GitRepo & BuildLog API
|
||||||
|
* Pagination using `limit` & `offset` query params
|
||||||
|
* GitRepo: filter by repo
|
||||||
|
* BuildLog: filter by start & end date, repo, exit code & arch
|
||||||
|
* CLI flags to take advantage of above API improvements
|
||||||
|
* Added CLI command to generate all man pages
|
||||||
|
* PKGBUILDs now install man pages
|
||||||
|
* Hosted CLI man pages ([vieter(1)](https://rustybever.be/man/vieter/vieter.1.html))
|
||||||
|
* Proper HTTP API docs ([link](https://rustybever.be/docs/vieter/api/))
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Packages from target repo are available during builds
|
||||||
|
* This can be used as a basic way to support AUR dependencies, by adding
|
||||||
|
the dependencies to the same repository
|
||||||
|
* Every build now updates its packages first instead of solely relying on the
|
||||||
|
updated builder image
|
||||||
|
* Build logs now show commands being executed
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* `POST /api/logs` now correctly uses epoch timestamps instead of strings
|
||||||
|
|
||||||
|
## [0.3.0-alpha.2](https://git.rustybever.be/vieter-v/vieter/src/tag/0.3.0-alpha.2)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Web API for adding & querying build logs
|
||||||
|
* CLI commands to access build logs API
|
||||||
|
* Cron build logs are uploaded to above API
|
||||||
|
* Proper ASCII table output in CLI
|
||||||
|
* `vieter repos build id` command to run builds locally
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
* `vieter build` command
|
||||||
|
* This command was used alongside cron for periodic builds, but this has
|
||||||
|
been replaced by `vieter cron`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* `vieter build` command now only builds a single repository & uploads the
|
||||||
|
build logs
|
||||||
|
* Official Arch packages are now split between `vieter` & `vieter-git`
|
||||||
|
* `vieter` is the latest release
|
||||||
|
* `vieter-git` is the latest commit on the dev branch
|
||||||
|
* Full refactor of Docker socket code
|
||||||
|
|
||||||
|
## [0.3.0-alpha.1](https://git.rustybever.be/vieter-v/vieter/src/tag/0.3.0-alpha.1)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Switched from compiler fork to fully vanilla compiler mirror
|
||||||
|
* `download_dir`, `repos_file` & `repos_dir` config values have been replaced
|
||||||
|
with `data_dir`
|
||||||
|
* Storage of metadata (e.g. Git repositories) is now done using Sqlite
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* Implemented own cron daemon for builder
|
||||||
|
* Build schedule can be configured globally or individually per repository
|
||||||
|
* Added CLI command to show detailed information per repo
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Binary no longer panics when an env var is missing
|
||||||
|
|
||||||
|
## [0.2.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.2.0)
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
@ -41,13 +224,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
* 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/Chewing_Bever/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/Chewing_Bever/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
|
||||||
|
|
||||||
|
|
20
Dockerfile
20
Dockerfile
|
@ -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,7 +23,8 @@ 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 \
|
||||||
LDFLAGS='-lz -lbz2 -llzma -lexpat -lzstd -llz4 -static' make prod && \
|
cd src && v install && cd .. && \
|
||||||
|
LDFLAGS='-lz -lbz2 -llzma -lexpat -lzstd -llz4 -lsqlite3 -static' make prod && \
|
||||||
mv pvieter vieter ; \
|
mv pvieter vieter ; \
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -31,22 +32,13 @@ RUN if [ -n "${CI_COMMIT_SHA}" ]; then \
|
||||||
FROM busybox:1.35.0
|
FROM busybox:1.35.0
|
||||||
|
|
||||||
ENV PATH=/bin \
|
ENV PATH=/bin \
|
||||||
VIETER_REPOS_DIR=/data/repos \
|
VIETER_DATA_DIR=/data \
|
||||||
VIETER_PKG_DIR=/data/pkgs \
|
VIETER_PKG_DIR=/data/pkgs
|
||||||
VIETER_DOWNLOAD_DIR=/data/downloads \
|
|
||||||
VIETER_REPOS_FILE=/data/repos.json
|
|
||||||
|
|
||||||
COPY --from=builder /app/dumb-init /app/vieter /bin/
|
COPY --from=builder /app/dumb-init /app/vieter /bin/
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s \
|
|
||||||
--timeout=3s \
|
|
||||||
--start-period=5s \
|
|
||||||
CMD /bin/wget --spider http://localhost:8000/health || exit 1
|
|
||||||
|
|
||||||
RUN mkdir /data && \
|
RUN mkdir /data && \
|
||||||
chown -R www-data:www-data /data && \
|
chown -R www-data:www-data /data
|
||||||
mkdir -p '/var/spool/cron/crontabs' && \
|
|
||||||
echo '0 3 * * * /bin/vieter build' | crontab -
|
|
||||||
|
|
||||||
WORKDIR /data
|
WORKDIR /data
|
||||||
|
|
||||||
|
|
80
Makefile
80
Makefile
|
@ -1,15 +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
|
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
|
||||||
|
@ -17,55 +22,90 @@ 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
|
||||||
.PHONY: gdb
|
.PHONY: gdb
|
||||||
gdb: dvieter
|
gdb: dvieter
|
||||||
gdb --args './dvieter -f vieter.toml server'
|
gdb --args ./dvieter -f vieter.toml server
|
||||||
|
|
||||||
# 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:
|
c: $(SOURCES) libvieter
|
||||||
$(V) -o vieter.c $(SRC_DIR)
|
$(V) -o vieter.c $(SRC_DIR)
|
||||||
|
|
||||||
|
|
||||||
# =====EXECUTION=====
|
# =====EXECUTION=====
|
||||||
# Run the server in the default 'data' directory
|
# Run the server in the default 'data' directory
|
||||||
.PHONY: run
|
.PHONY: run
|
||||||
run: vieter
|
run: vieter
|
||||||
./vieter -f vieter.toml server
|
./vieter -f vieter.toml server
|
||||||
|
|
||||||
.PHONY: run-prod
|
.PHONY: run-prod
|
||||||
run-prod: prod
|
run-prod: prod
|
||||||
./pvieter -f vieter.toml server
|
./pvieter -f vieter.toml server
|
||||||
|
|
||||||
|
|
||||||
|
# =====DOCS=====
|
||||||
|
.PHONY: docs
|
||||||
|
docs:
|
||||||
|
rm -rf 'docs/public'
|
||||||
|
cd docs && hugo
|
||||||
|
|
||||||
|
.PHONY: api-docs
|
||||||
|
api-docs:
|
||||||
|
rm -rf '$(SRC_DIR)/_docs'
|
||||||
|
cd '$(SRC_DIR)' && v doc -all -f html -m -readme .
|
||||||
|
|
||||||
|
.PHONY: man
|
||||||
|
man: vieter
|
||||||
|
rm -rf man
|
||||||
|
./vieter man man
|
||||||
|
|
||||||
|
|
||||||
# =====OTHER=====
|
# =====OTHER=====
|
||||||
|
# Linting
|
||||||
.PHONY: lint
|
.PHONY: lint
|
||||||
lint:
|
lint:
|
||||||
$(V) fmt -verify $(SRC_DIR)
|
$(V) fmt -verify $(SRC_DIR)
|
||||||
|
$(V) vet -W $(SRC_DIR)
|
||||||
|
$(V_PATH) missdoc -p $(SRC_DIR)
|
||||||
|
@ [ $$($(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)
|
||||||
|
|
||||||
.PHONY: vet
|
|
||||||
vet:
|
|
||||||
$(V) vet -W $(SRC_DIR)
|
|
||||||
|
|
||||||
# Build & patch the V compiler
|
# Testing
|
||||||
.PHONY: v
|
.PHONY: test
|
||||||
v: v/v
|
test: libvieter
|
||||||
v/v:
|
$(V) -g test $(SRC_DIR)
|
||||||
git clone --single-branch --branch patches https://git.rustybever.be/Chewing_Bever/vieter-v v
|
|
||||||
make -C v
|
|
||||||
|
|
||||||
|
|
||||||
|
# Cleaning
|
||||||
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -rf 'data' 'vieter' 'dvieter' 'pvieter' 'vieter.c' 'dvieterctl' 'vieterctl' 'pkg' 'src/vieter'
|
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=====
|
||||||
|
.PHONY: autofree
|
||||||
|
autofree: afvieter
|
||||||
|
afvieter: $(SOURCES)
|
||||||
|
$(V) -showcc -autofree -o afvieter $(SRC_DIR)
|
||||||
|
|
||||||
|
.PHONY: skip-unused
|
||||||
|
skip-unused: suvieter
|
||||||
|
suvieter: $(SOURCES)
|
||||||
|
$(V) -skip-unused -o suvieter $(SRC_DIR)
|
||||||
|
|
48
PKGBUILD
48
PKGBUILD
|
@ -1,34 +1,52 @@
|
||||||
|
# vim: ft=bash
|
||||||
# Maintainer: Jef Roosens
|
# Maintainer: Jef Roosens
|
||||||
|
|
||||||
pkgbase='vieter'
|
pkgbase='vieter'
|
||||||
pkgname='vieter'
|
pkgname='vieter'
|
||||||
pkgver=0.1.0.rc1.r117.gc3ac00f
|
pkgver='0.6.0'
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
depends=('glibc' 'openssl' 'libarchive' 'gc')
|
pkgdesc="Lightweight Arch repository server & package build system"
|
||||||
makedepends=('git' 'gcc')
|
depends=('glibc' 'openssl' 'libarchive' 'sqlite')
|
||||||
arch=('x86_64' 'aarch64' 'armv7')
|
makedepends=('git' 'vieter-vlang')
|
||||||
url='https://git.rustybever.be/Chewing_Bever/vieter'
|
arch=('x86_64' 'aarch64')
|
||||||
|
url='https://git.rustybever.be/vieter-v/vieter'
|
||||||
license=('AGPL3')
|
license=('AGPL3')
|
||||||
source=($pkgname::git+https://git.rustybever.be/Chewing_Bever/vieter#branch=dev)
|
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')
|
||||||
|
|
||||||
pkgver() {
|
prepare() {
|
||||||
cd "$pkgname"
|
cd "${pkgname}"
|
||||||
git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g'
|
|
||||||
|
# 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"
|
||||||
|
|
||||||
# Build the compiler
|
|
||||||
CFLAGS= make v
|
|
||||||
|
|
||||||
make prod
|
make prod
|
||||||
|
|
||||||
|
# The default CFLAGS for some reason causes vieter to segfault if used
|
||||||
|
# inside the PKGBUILD. As a workaround, we use tcc to build a debug build
|
||||||
|
# that does work, so we can generate the manpages.
|
||||||
|
CFLAGS= LDFLAGS= make man
|
||||||
}
|
}
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
pkgdesc="Vieter is a lightweight implementation of an Arch repository server."
|
|
||||||
install -dm755 "$pkgdir/usr/bin"
|
install -dm755 "$pkgdir/usr/bin"
|
||||||
|
install -Dm755 "$pkgname/pvieter" "$pkgdir/usr/bin/vieter"
|
||||||
|
|
||||||
install -Dm755 "$pkgbase/pvieter" "$pkgdir/usr/bin/vieter"
|
install -dm755 "$pkgdir/usr/share/man/man1"
|
||||||
|
install -Dm644 "$pkgname/man"/*.1 "$pkgdir/usr/share/man/man1"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# vim: ft=bash
|
||||||
|
# Maintainer: Jef Roosens
|
||||||
|
|
||||||
|
pkgbase='vieter-git'
|
||||||
|
pkgname='vieter-git'
|
||||||
|
pkgver=0.2.0.r25.g20112b8
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="Lightweight Arch repository server & package build system (development version)"
|
||||||
|
depends=('glibc' 'openssl' 'libarchive' 'sqlite')
|
||||||
|
makedepends=('git' 'vieter-vlang')
|
||||||
|
arch=('x86_64' 'aarch64')
|
||||||
|
url='https://git.rustybever.be/vieter-v/vieter'
|
||||||
|
license=('AGPL3')
|
||||||
|
source=(
|
||||||
|
"${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')
|
||||||
|
conflicts=('vieter')
|
||||||
|
|
||||||
|
pkgver() {
|
||||||
|
cd "${pkgname}"
|
||||||
|
|
||||||
|
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() {
|
||||||
|
export VMODULES="${srcdir}/.vmodules"
|
||||||
|
|
||||||
|
cd "${pkgname}"
|
||||||
|
|
||||||
|
make prod
|
||||||
|
|
||||||
|
# The default CFLAGS for some reason causes vieter to segfault if used
|
||||||
|
# inside the PKGBUILD. As a workaround, we use tcc to build a debug build
|
||||||
|
# that does work, so we can generate the manpages.
|
||||||
|
CFLAGS= LDFLAGS= make man
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
install -dm755 "${pkgdir}/usr/bin"
|
||||||
|
install -Dm755 "${pkgname}/pvieter" "${pkgdir}/usr/bin/vieter"
|
||||||
|
|
||||||
|
install -dm755 "${pkgdir}/usr/share/man/man1"
|
||||||
|
install -Dm644 "${pkgname}/man"/*.1 "${pkgdir}/usr/share/man/man1"
|
||||||
|
}
|
79
README.md
79
README.md
|
@ -1,8 +1,11 @@
|
||||||
# Vieter
|
# Vieter
|
||||||
|
|
||||||
## Documentation
|
I host documentation for Vieter over at https://rustybever.be/docs/vieter/. API
|
||||||
|
documentation for the current codebase can be found at
|
||||||
|
https://rustybever.be/api-docs/vieter/.
|
||||||
|
|
||||||
I host documentation for Vieter over at https://rustybever.be/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
|
||||||
|
|
||||||
|
@ -18,17 +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
|
||||||
### Custom Compiler
|
|
||||||
|
|
||||||
Currently, this program only works with a very slightly modified version of the
|
|
||||||
V standard library, and therefore the compiler. The source code for this fork
|
|
||||||
can be found [here](https://git.rustybever.be/Chewing_Bever/vieter-v). You can
|
|
||||||
obtain this modified version of the compiler by running `make v`, which will
|
|
||||||
clone & build the compiler. Afterwards, all make commands that require the V
|
|
||||||
compiler will use this new binary. I try to keep this fork as up to date with
|
|
||||||
upstream as possible.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
@ -42,11 +36,62 @@ upstream as possible.
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
In order to build Vieter, you'll need a couple of libraries:
|
Besides a V installer, Vieter also requires the following libraries to work:
|
||||||
|
|
||||||
* gc
|
|
||||||
* libarchive
|
* libarchive
|
||||||
* openssl
|
* openssl
|
||||||
|
* sqlite3
|
||||||
|
|
||||||
Before building Vieter, you'll have to build the compiler using `make v`.
|
Vieter also depends on some external V modules which you can install using `cd
|
||||||
Afterwards, run `make` to build the debug binary.
|
src && v install`. Make sure to keep these dependencies up to date using `v
|
||||||
|
update`.
|
||||||
|
|
||||||
|
### Compiler
|
||||||
|
|
||||||
|
V is developed using a specific compiler commit that is usually updated
|
||||||
|
whenever a new version is released. Information on this can be found in the
|
||||||
|
[tools](https://git.rustybever.be/vieter-v/tools) repository.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
If you wish to contribute to the project, please take note of the following:
|
||||||
|
|
||||||
|
* Rebase instead of merging whenever possible, e.g. when updating your branch
|
||||||
|
with the dev branch.
|
||||||
|
* Please follow the
|
||||||
|
[Conventional Commits](https://www.conventionalcommits.org/) style for your
|
||||||
|
commit messages.
|
||||||
|
|
||||||
|
### Writing documentation
|
||||||
|
|
||||||
|
The `docs` directory contains a Hugo site consisting of all user &
|
||||||
|
administrator documentation. `docs/api` on the other hand is a
|
||||||
|
[Slate](https://github.com/slatedocs/slate) project describing the HTTP web
|
||||||
|
API.
|
||||||
|
|
||||||
|
To modify the Hugo documentation, you'll need to install Hugo. Afterwards, you
|
||||||
|
can use the following commands inside the `docs` directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# Build the documentation
|
||||||
|
hugo
|
||||||
|
|
||||||
|
# Host an auto-refreshing web server with the documentation. Important to note
|
||||||
|
# is that the files will be at `http://localhost:1313/docs/vieter` instead of
|
||||||
|
# just `http://localhost:1313/`
|
||||||
|
hugo server
|
||||||
|
```
|
||||||
|
|
||||||
|
For the Slate docs, I personally just start a docker container:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker run \
|
||||||
|
--rm \
|
||||||
|
-p 4567:4567 \
|
||||||
|
--name slate \
|
||||||
|
-v $(pwd)/docs/api/source:/srv/slate/source slatedocs/slate serve
|
||||||
|
```
|
||||||
|
|
||||||
|
This will make the Slate docs available at http://localhost:4567. Sadly, this
|
||||||
|
server doesn't auto-refresh, so you'll have to manually refresh your browser
|
||||||
|
every time you make a change.
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
.hugo_build.lock
|
||||||
|
/public/
|
|
@ -0,0 +1,17 @@
|
||||||
|
platform: 'linux/amd64'
|
||||||
|
branches: 'main'
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
release:
|
||||||
|
image: 'klakegg/hugo:alpine'
|
||||||
|
commands:
|
||||||
|
- apk add git
|
||||||
|
- hugo
|
||||||
|
- 'cd public && tar czvf ../public.tar.gz *'
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
image: 'curlimages/curl'
|
||||||
|
secrets:
|
||||||
|
- 'api_key'
|
||||||
|
commands:
|
||||||
|
- 'curl -XPOST --fail -s -H "Authorization: Bearer $API_KEY" -T public.tar.gz https://rustybever.be/api/deploy?dir=docs'
|
|
@ -0,0 +1,9 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) <year> <copyright holders>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,3 @@
|
||||||
|
# docs
|
||||||
|
|
||||||
|
Repository containing docs for various personal projects I've made.
|
|
@ -0,0 +1,12 @@
|
||||||
|
.git/
|
||||||
|
.github/
|
||||||
|
build/
|
||||||
|
.editorconfig
|
||||||
|
.gitattributes
|
||||||
|
.gitignore
|
||||||
|
CHANGELOG.md
|
||||||
|
CODE_OF_CONDUCT.md
|
||||||
|
deploy.sh
|
||||||
|
font-selection.json
|
||||||
|
README.md
|
||||||
|
Vagrantfile
|
|
@ -0,0 +1,18 @@
|
||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# Top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
# Unix-style newlines with a newline ending every file
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.rb]
|
||||||
|
charset = utf-8
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
|
@ -0,0 +1 @@
|
||||||
|
source/javascripts/lib/* linguist-vendored
|
|
@ -0,0 +1,27 @@
|
||||||
|
*.gem
|
||||||
|
*.rbc
|
||||||
|
.bundle
|
||||||
|
.config
|
||||||
|
coverage
|
||||||
|
InstalledFiles
|
||||||
|
lib/bundler/man
|
||||||
|
pkg
|
||||||
|
rdoc
|
||||||
|
spec/reports
|
||||||
|
test/tmp
|
||||||
|
test/version_tmp
|
||||||
|
tmp
|
||||||
|
*.DS_STORE
|
||||||
|
build/
|
||||||
|
.cache
|
||||||
|
.vagrant
|
||||||
|
.sass-cache
|
||||||
|
|
||||||
|
# YARD artifacts
|
||||||
|
.yardoc
|
||||||
|
_yardoc
|
||||||
|
doc/
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Vagrant artifacts
|
||||||
|
ubuntu-*-console.log
|
|
@ -0,0 +1,13 @@
|
||||||
|
ruby '>= 2.6'
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
# Middleman
|
||||||
|
gem 'middleman', '~> 4.4'
|
||||||
|
gem 'middleman-syntax', '~> 3.2'
|
||||||
|
gem 'middleman-autoprefixer', '~> 3.0'
|
||||||
|
gem 'middleman-sprockets', '~> 4.1'
|
||||||
|
gem 'rouge', '~> 3.21'
|
||||||
|
gem 'redcarpet', '~> 3.5.0'
|
||||||
|
gem 'nokogiri', '~> 1.13.3'
|
||||||
|
gem 'sass'
|
||||||
|
gem 'webrick'
|
|
@ -0,0 +1,145 @@
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
activesupport (6.1.4.1)
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
i18n (>= 1.6, < 2)
|
||||||
|
minitest (>= 5.1)
|
||||||
|
tzinfo (~> 2.0)
|
||||||
|
zeitwerk (~> 2.3)
|
||||||
|
addressable (2.8.0)
|
||||||
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
|
autoprefixer-rails (10.2.5.0)
|
||||||
|
execjs (< 2.8.0)
|
||||||
|
backports (3.21.0)
|
||||||
|
coffee-script (2.4.1)
|
||||||
|
coffee-script-source
|
||||||
|
execjs
|
||||||
|
coffee-script-source (1.12.2)
|
||||||
|
concurrent-ruby (1.1.9)
|
||||||
|
contracts (0.13.0)
|
||||||
|
dotenv (2.7.6)
|
||||||
|
erubis (2.7.0)
|
||||||
|
execjs (2.7.0)
|
||||||
|
fast_blank (1.0.1)
|
||||||
|
fastimage (2.2.5)
|
||||||
|
ffi (1.15.4)
|
||||||
|
haml (5.2.2)
|
||||||
|
temple (>= 0.8.0)
|
||||||
|
tilt
|
||||||
|
hamster (3.0.0)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
hashie (3.6.0)
|
||||||
|
i18n (1.6.0)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
kramdown (2.3.1)
|
||||||
|
rexml
|
||||||
|
listen (3.0.8)
|
||||||
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||||
|
rb-inotify (~> 0.9, >= 0.9.7)
|
||||||
|
memoist (0.16.2)
|
||||||
|
middleman (4.4.2)
|
||||||
|
coffee-script (~> 2.2)
|
||||||
|
haml (>= 4.0.5)
|
||||||
|
kramdown (>= 2.3.0)
|
||||||
|
middleman-cli (= 4.4.2)
|
||||||
|
middleman-core (= 4.4.2)
|
||||||
|
middleman-autoprefixer (3.0.0)
|
||||||
|
autoprefixer-rails (~> 10.0)
|
||||||
|
middleman-core (>= 4.0.0)
|
||||||
|
middleman-cli (4.4.2)
|
||||||
|
thor (>= 0.17.0, < 2.0)
|
||||||
|
middleman-core (4.4.2)
|
||||||
|
activesupport (>= 6.1, < 7.0)
|
||||||
|
addressable (~> 2.4)
|
||||||
|
backports (~> 3.6)
|
||||||
|
bundler (~> 2.0)
|
||||||
|
contracts (~> 0.13.0)
|
||||||
|
dotenv
|
||||||
|
erubis
|
||||||
|
execjs (~> 2.0)
|
||||||
|
fast_blank
|
||||||
|
fastimage (~> 2.0)
|
||||||
|
hamster (~> 3.0)
|
||||||
|
hashie (~> 3.4)
|
||||||
|
i18n (~> 1.6.0)
|
||||||
|
listen (~> 3.0.0)
|
||||||
|
memoist (~> 0.14)
|
||||||
|
padrino-helpers (~> 0.15.0)
|
||||||
|
parallel
|
||||||
|
rack (>= 1.4.5, < 3)
|
||||||
|
sassc (~> 2.0)
|
||||||
|
servolux
|
||||||
|
tilt (~> 2.0.9)
|
||||||
|
toml
|
||||||
|
uglifier (~> 3.0)
|
||||||
|
webrick
|
||||||
|
middleman-sprockets (4.1.1)
|
||||||
|
middleman-core (~> 4.0)
|
||||||
|
sprockets (>= 3.0)
|
||||||
|
middleman-syntax (3.2.0)
|
||||||
|
middleman-core (>= 3.2)
|
||||||
|
rouge (~> 3.2)
|
||||||
|
mini_portile2 (2.8.0)
|
||||||
|
minitest (5.14.4)
|
||||||
|
nokogiri (1.13.4)
|
||||||
|
mini_portile2 (~> 2.8.0)
|
||||||
|
racc (~> 1.4)
|
||||||
|
padrino-helpers (0.15.1)
|
||||||
|
i18n (>= 0.6.7, < 2)
|
||||||
|
padrino-support (= 0.15.1)
|
||||||
|
tilt (>= 1.4.1, < 3)
|
||||||
|
padrino-support (0.15.1)
|
||||||
|
parallel (1.21.0)
|
||||||
|
parslet (2.0.0)
|
||||||
|
public_suffix (4.0.6)
|
||||||
|
racc (1.6.0)
|
||||||
|
rack (2.2.3)
|
||||||
|
rb-fsevent (0.11.0)
|
||||||
|
rb-inotify (0.10.1)
|
||||||
|
ffi (~> 1.0)
|
||||||
|
redcarpet (3.5.1)
|
||||||
|
rexml (3.2.5)
|
||||||
|
rouge (3.28.0)
|
||||||
|
sass (3.7.4)
|
||||||
|
sass-listen (~> 4.0.0)
|
||||||
|
sass-listen (4.0.0)
|
||||||
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||||
|
rb-inotify (~> 0.9, >= 0.9.7)
|
||||||
|
sassc (2.4.0)
|
||||||
|
ffi (~> 1.9)
|
||||||
|
servolux (0.13.0)
|
||||||
|
sprockets (3.7.2)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
rack (> 1, < 3)
|
||||||
|
temple (0.8.2)
|
||||||
|
thor (1.1.0)
|
||||||
|
tilt (2.0.10)
|
||||||
|
toml (0.3.0)
|
||||||
|
parslet (>= 1.8.0, < 3.0.0)
|
||||||
|
tzinfo (2.0.4)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
uglifier (3.2.0)
|
||||||
|
execjs (>= 0.3.0, < 3)
|
||||||
|
webrick (1.7.0)
|
||||||
|
zeitwerk (2.5.1)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
middleman (~> 4.4)
|
||||||
|
middleman-autoprefixer (~> 3.0)
|
||||||
|
middleman-sprockets (~> 4.1)
|
||||||
|
middleman-syntax (~> 3.2)
|
||||||
|
nokogiri (~> 1.13.3)
|
||||||
|
redcarpet (~> 3.5.0)
|
||||||
|
rouge (~> 3.21)
|
||||||
|
sass
|
||||||
|
webrick
|
||||||
|
|
||||||
|
RUBY VERSION
|
||||||
|
ruby 2.7.2p137
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.2.22
|
|
@ -0,0 +1,63 @@
|
||||||
|
# Unique header generation
|
||||||
|
require './lib/unique_head.rb'
|
||||||
|
|
||||||
|
# Markdown
|
||||||
|
set :markdown_engine, :redcarpet
|
||||||
|
set :markdown,
|
||||||
|
fenced_code_blocks: true,
|
||||||
|
smartypants: true,
|
||||||
|
disable_indented_code_blocks: true,
|
||||||
|
prettify: true,
|
||||||
|
strikethrough: true,
|
||||||
|
tables: true,
|
||||||
|
with_toc_data: true,
|
||||||
|
no_intra_emphasis: true,
|
||||||
|
renderer: UniqueHeadCounter
|
||||||
|
|
||||||
|
# Assets
|
||||||
|
set :css_dir, 'stylesheets'
|
||||||
|
set :js_dir, 'javascripts'
|
||||||
|
set :images_dir, 'images'
|
||||||
|
set :fonts_dir, 'fonts'
|
||||||
|
|
||||||
|
# Activate the syntax highlighter
|
||||||
|
activate :syntax
|
||||||
|
ready do
|
||||||
|
require './lib/monokai_sublime_slate.rb'
|
||||||
|
require './lib/multilang.rb'
|
||||||
|
end
|
||||||
|
|
||||||
|
activate :sprockets
|
||||||
|
|
||||||
|
activate :autoprefixer do |config|
|
||||||
|
config.browsers = ['last 2 version', 'Firefox ESR']
|
||||||
|
config.cascade = false
|
||||||
|
config.inline = true
|
||||||
|
end
|
||||||
|
|
||||||
|
# Github pages require relative links
|
||||||
|
activate :relative_assets
|
||||||
|
set :relative_links, true
|
||||||
|
|
||||||
|
# Build Configuration
|
||||||
|
configure :build do
|
||||||
|
# We do want to hash woff and woff2 as there's a bug where woff2 will use
|
||||||
|
# woff asset hash which breaks things. Trying to use a combination of ignore and
|
||||||
|
# rewrite_ignore does not work as it conflicts weirdly with relative_assets. Disabling
|
||||||
|
# the .woff2 extension only does not work as .woff will still activate it so have to
|
||||||
|
# have both. See https://github.com/slatedocs/slate/issues/1171 for more details.
|
||||||
|
activate :asset_hash, :exts => app.config[:asset_extensions] - %w[.woff .woff2]
|
||||||
|
# If you're having trouble with Middleman hanging, commenting
|
||||||
|
# out the following two lines has been known to help
|
||||||
|
activate :minify_css
|
||||||
|
activate :minify_javascript
|
||||||
|
# activate :gzip
|
||||||
|
end
|
||||||
|
|
||||||
|
# Deploy Configuration
|
||||||
|
# If you want Middleman to listen on a different port, you can set that below
|
||||||
|
set :port, 4567
|
||||||
|
|
||||||
|
helpers do
|
||||||
|
require './lib/toc_data.rb'
|
||||||
|
end
|
|
@ -0,0 +1,148 @@
|
||||||
|
{
|
||||||
|
"IcoMoonType": "selection",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"paths": [
|
||||||
|
"M438.857 73.143q119.429 0 220.286 58.857t159.714 159.714 58.857 220.286-58.857 220.286-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857zM512 785.714v-108.571q0-8-5.143-13.429t-12.571-5.429h-109.714q-7.429 0-13.143 5.714t-5.714 13.143v108.571q0 7.429 5.714 13.143t13.143 5.714h109.714q7.429 0 12.571-5.429t5.143-13.429zM510.857 589.143l10.286-354.857q0-6.857-5.714-10.286-5.714-4.571-13.714-4.571h-125.714q-8 0-13.714 4.571-5.714 3.429-5.714 10.286l9.714 354.857q0 5.714 5.714 10t13.714 4.286h105.714q8 0 13.429-4.286t6-10z"
|
||||||
|
],
|
||||||
|
"attrs": [],
|
||||||
|
"isMulticolor": false,
|
||||||
|
"tags": [
|
||||||
|
"exclamation-circle"
|
||||||
|
],
|
||||||
|
"defaultCode": 61546,
|
||||||
|
"grid": 14
|
||||||
|
},
|
||||||
|
"attrs": [],
|
||||||
|
"properties": {
|
||||||
|
"id": 100,
|
||||||
|
"order": 4,
|
||||||
|
"prevSize": 28,
|
||||||
|
"code": 58880,
|
||||||
|
"name": "exclamation-sign",
|
||||||
|
"ligatures": ""
|
||||||
|
},
|
||||||
|
"setIdx": 0,
|
||||||
|
"iconIdx": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"paths": [
|
||||||
|
"M585.143 786.286v-91.429q0-8-5.143-13.143t-13.143-5.143h-54.857v-292.571q0-8-5.143-13.143t-13.143-5.143h-182.857q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h54.857v182.857h-54.857q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h256q8 0 13.143-5.143t5.143-13.143zM512 274.286v-91.429q0-8-5.143-13.143t-13.143-5.143h-109.714q-8 0-13.143 5.143t-5.143 13.143v91.429q0 8 5.143 13.143t13.143 5.143h109.714q8 0 13.143-5.143t5.143-13.143zM877.714 512q0 119.429-58.857 220.286t-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857 220.286 58.857 159.714 159.714 58.857 220.286z"
|
||||||
|
],
|
||||||
|
"attrs": [],
|
||||||
|
"isMulticolor": false,
|
||||||
|
"tags": [
|
||||||
|
"info-circle"
|
||||||
|
],
|
||||||
|
"defaultCode": 61530,
|
||||||
|
"grid": 14
|
||||||
|
},
|
||||||
|
"attrs": [],
|
||||||
|
"properties": {
|
||||||
|
"id": 85,
|
||||||
|
"order": 3,
|
||||||
|
"name": "info-sign",
|
||||||
|
"prevSize": 28,
|
||||||
|
"code": 58882
|
||||||
|
},
|
||||||
|
"setIdx": 0,
|
||||||
|
"iconIdx": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"paths": [
|
||||||
|
"M733.714 419.429q0-16-10.286-26.286l-52-51.429q-10.857-10.857-25.714-10.857t-25.714 10.857l-233.143 232.571-129.143-129.143q-10.857-10.857-25.714-10.857t-25.714 10.857l-52 51.429q-10.286 10.286-10.286 26.286 0 15.429 10.286 25.714l206.857 206.857q10.857 10.857 25.714 10.857 15.429 0 26.286-10.857l310.286-310.286q10.286-10.286 10.286-25.714zM877.714 512q0 119.429-58.857 220.286t-159.714 159.714-220.286 58.857-220.286-58.857-159.714-159.714-58.857-220.286 58.857-220.286 159.714-159.714 220.286-58.857 220.286 58.857 159.714 159.714 58.857 220.286z"
|
||||||
|
],
|
||||||
|
"attrs": [],
|
||||||
|
"isMulticolor": false,
|
||||||
|
"tags": [
|
||||||
|
"check-circle"
|
||||||
|
],
|
||||||
|
"defaultCode": 61528,
|
||||||
|
"grid": 14
|
||||||
|
},
|
||||||
|
"attrs": [],
|
||||||
|
"properties": {
|
||||||
|
"id": 83,
|
||||||
|
"order": 9,
|
||||||
|
"prevSize": 28,
|
||||||
|
"code": 58886,
|
||||||
|
"name": "ok-sign"
|
||||||
|
},
|
||||||
|
"setIdx": 0,
|
||||||
|
"iconIdx": 6
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"paths": [
|
||||||
|
"M658.286 475.429q0-105.714-75.143-180.857t-180.857-75.143-180.857 75.143-75.143 180.857 75.143 180.857 180.857 75.143 180.857-75.143 75.143-180.857zM950.857 950.857q0 29.714-21.714 51.429t-51.429 21.714q-30.857 0-51.429-21.714l-196-195.429q-102.286 70.857-228 70.857-81.714 0-156.286-31.714t-128.571-85.714-85.714-128.571-31.714-156.286 31.714-156.286 85.714-128.571 128.571-85.714 156.286-31.714 156.286 31.714 128.571 85.714 85.714 128.571 31.714 156.286q0 125.714-70.857 228l196 196q21.143 21.143 21.143 51.429z"
|
||||||
|
],
|
||||||
|
"width": 951,
|
||||||
|
"attrs": [],
|
||||||
|
"isMulticolor": false,
|
||||||
|
"tags": [
|
||||||
|
"search"
|
||||||
|
],
|
||||||
|
"defaultCode": 61442,
|
||||||
|
"grid": 14
|
||||||
|
},
|
||||||
|
"attrs": [],
|
||||||
|
"properties": {
|
||||||
|
"id": 2,
|
||||||
|
"order": 1,
|
||||||
|
"prevSize": 28,
|
||||||
|
"code": 58887,
|
||||||
|
"name": "icon-search"
|
||||||
|
},
|
||||||
|
"setIdx": 0,
|
||||||
|
"iconIdx": 7
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"height": 1024,
|
||||||
|
"metadata": {
|
||||||
|
"name": "slate",
|
||||||
|
"license": "SIL OFL 1.1"
|
||||||
|
},
|
||||||
|
"preferences": {
|
||||||
|
"showGlyphs": true,
|
||||||
|
"showQuickUse": true,
|
||||||
|
"showQuickUse2": true,
|
||||||
|
"showSVGs": true,
|
||||||
|
"fontPref": {
|
||||||
|
"prefix": "icon-",
|
||||||
|
"metadata": {
|
||||||
|
"fontFamily": "slate",
|
||||||
|
"majorVersion": 1,
|
||||||
|
"minorVersion": 0,
|
||||||
|
"description": "Based on FontAwesome",
|
||||||
|
"license": "SIL OFL 1.1"
|
||||||
|
},
|
||||||
|
"metrics": {
|
||||||
|
"emSize": 1024,
|
||||||
|
"baseline": 6.25,
|
||||||
|
"whitespace": 50
|
||||||
|
},
|
||||||
|
"resetPoint": 58880,
|
||||||
|
"showSelector": false,
|
||||||
|
"selector": "class",
|
||||||
|
"classSelector": ".icon",
|
||||||
|
"showMetrics": false,
|
||||||
|
"showMetadata": true,
|
||||||
|
"showVersion": true,
|
||||||
|
"ie7": false
|
||||||
|
},
|
||||||
|
"imagePref": {
|
||||||
|
"prefix": "icon-",
|
||||||
|
"png": true,
|
||||||
|
"useClassSelector": true,
|
||||||
|
"color": 4473924,
|
||||||
|
"bgColor": 16777215
|
||||||
|
},
|
||||||
|
"historySize": 100,
|
||||||
|
"showCodes": true,
|
||||||
|
"gridSize": 16,
|
||||||
|
"showLiga": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
# -*- coding: utf-8 -*- #
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# this is based on https://github.com/rouge-ruby/rouge/blob/master/lib/rouge/themes/monokai_sublime.rb
|
||||||
|
# but without the added background, and changed styling for JSON keys to be soft_yellow instead of white
|
||||||
|
|
||||||
|
module Rouge
|
||||||
|
module Themes
|
||||||
|
class MonokaiSublimeSlate < CSSTheme
|
||||||
|
name 'monokai.sublime.slate'
|
||||||
|
|
||||||
|
palette :black => '#000000'
|
||||||
|
palette :bright_green => '#a6e22e'
|
||||||
|
palette :bright_pink => '#f92672'
|
||||||
|
palette :carmine => '#960050'
|
||||||
|
palette :dark => '#49483e'
|
||||||
|
palette :dark_grey => '#888888'
|
||||||
|
palette :dark_red => '#aa0000'
|
||||||
|
palette :dimgrey => '#75715e'
|
||||||
|
palette :emperor => '#555555'
|
||||||
|
palette :grey => '#999999'
|
||||||
|
palette :light_grey => '#aaaaaa'
|
||||||
|
palette :light_violet => '#ae81ff'
|
||||||
|
palette :soft_cyan => '#66d9ef'
|
||||||
|
palette :soft_yellow => '#e6db74'
|
||||||
|
palette :very_dark => '#1e0010'
|
||||||
|
palette :whitish => '#f8f8f2'
|
||||||
|
palette :orange => '#f6aa11'
|
||||||
|
palette :white => '#ffffff'
|
||||||
|
|
||||||
|
style Generic::Heading, :fg => :grey
|
||||||
|
style Literal::String::Regex, :fg => :orange
|
||||||
|
style Generic::Output, :fg => :dark_grey
|
||||||
|
style Generic::Prompt, :fg => :emperor
|
||||||
|
style Generic::Strong, :bold => false
|
||||||
|
style Generic::Subheading, :fg => :light_grey
|
||||||
|
style Name::Builtin, :fg => :orange
|
||||||
|
style Comment::Multiline,
|
||||||
|
Comment::Preproc,
|
||||||
|
Comment::Single,
|
||||||
|
Comment::Special,
|
||||||
|
Comment, :fg => :dimgrey
|
||||||
|
style Error,
|
||||||
|
Generic::Error,
|
||||||
|
Generic::Traceback, :fg => :carmine
|
||||||
|
style Generic::Deleted,
|
||||||
|
Generic::Inserted,
|
||||||
|
Generic::Emph, :fg => :dark
|
||||||
|
style Keyword::Constant,
|
||||||
|
Keyword::Declaration,
|
||||||
|
Keyword::Reserved,
|
||||||
|
Name::Constant,
|
||||||
|
Keyword::Type, :fg => :soft_cyan
|
||||||
|
style Literal::Number::Float,
|
||||||
|
Literal::Number::Hex,
|
||||||
|
Literal::Number::Integer::Long,
|
||||||
|
Literal::Number::Integer,
|
||||||
|
Literal::Number::Oct,
|
||||||
|
Literal::Number,
|
||||||
|
Literal::String::Char,
|
||||||
|
Literal::String::Escape,
|
||||||
|
Literal::String::Symbol, :fg => :light_violet
|
||||||
|
style Literal::String::Doc,
|
||||||
|
Literal::String::Double,
|
||||||
|
Literal::String::Backtick,
|
||||||
|
Literal::String::Heredoc,
|
||||||
|
Literal::String::Interpol,
|
||||||
|
Literal::String::Other,
|
||||||
|
Literal::String::Single,
|
||||||
|
Literal::String, :fg => :soft_yellow
|
||||||
|
style Name::Attribute,
|
||||||
|
Name::Class,
|
||||||
|
Name::Decorator,
|
||||||
|
Name::Exception,
|
||||||
|
Name::Function, :fg => :bright_green
|
||||||
|
style Name::Variable::Class,
|
||||||
|
Name::Namespace,
|
||||||
|
Name::Entity,
|
||||||
|
Name::Builtin::Pseudo,
|
||||||
|
Name::Variable::Global,
|
||||||
|
Name::Variable::Instance,
|
||||||
|
Name::Variable,
|
||||||
|
Text::Whitespace,
|
||||||
|
Text,
|
||||||
|
Name, :fg => :white
|
||||||
|
style Name::Label, :fg => :bright_pink
|
||||||
|
style Operator::Word,
|
||||||
|
Name::Tag,
|
||||||
|
Keyword,
|
||||||
|
Keyword::Namespace,
|
||||||
|
Keyword::Pseudo,
|
||||||
|
Operator, :fg => :bright_pink
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,16 @@
|
||||||
|
module Multilang
|
||||||
|
def block_code(code, full_lang_name)
|
||||||
|
if full_lang_name
|
||||||
|
parts = full_lang_name.split('--')
|
||||||
|
rouge_lang_name = (parts) ? parts[0] : "" # just parts[0] here causes null ref exception when no language specified
|
||||||
|
super(code, rouge_lang_name).sub("highlight #{rouge_lang_name}") do |match|
|
||||||
|
match + " tab-" + full_lang_name
|
||||||
|
end
|
||||||
|
else
|
||||||
|
super(code, full_lang_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
require 'middleman-core/renderers/redcarpet'
|
||||||
|
Middleman::Renderers::MiddlemanRedcarpetHTML.send :include, Multilang
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Nested unique header generation
|
||||||
|
require 'middleman-core/renderers/redcarpet'
|
||||||
|
|
||||||
|
class NestingUniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML
|
||||||
|
def initialize
|
||||||
|
super
|
||||||
|
@@headers_history = {} if !defined?(@@headers_history)
|
||||||
|
end
|
||||||
|
|
||||||
|
def header(text, header_level)
|
||||||
|
friendly_text = text.gsub(/<[^>]*>/,"").parameterize
|
||||||
|
@@headers_history[header_level] = text.parameterize
|
||||||
|
|
||||||
|
if header_level > 1
|
||||||
|
for i in (header_level - 1).downto(1)
|
||||||
|
friendly_text.prepend("#{@@headers_history[i]}-") if @@headers_history.key?(i)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return "<h#{header_level} id='#{friendly_text}'>#{text}</h#{header_level}>"
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,31 @@
|
||||||
|
require 'nokogiri'
|
||||||
|
|
||||||
|
def toc_data(page_content)
|
||||||
|
html_doc = Nokogiri::HTML::DocumentFragment.parse(page_content)
|
||||||
|
|
||||||
|
# get a flat list of headers
|
||||||
|
headers = []
|
||||||
|
html_doc.css('h1, h2, h3').each do |header|
|
||||||
|
headers.push({
|
||||||
|
id: header.attribute('id').to_s,
|
||||||
|
content: header.children,
|
||||||
|
title: header.children.to_s.gsub(/<[^>]*>/, ''),
|
||||||
|
level: header.name[1].to_i,
|
||||||
|
children: []
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
[3,2].each do |header_level|
|
||||||
|
header_to_nest = nil
|
||||||
|
headers = headers.reject do |header|
|
||||||
|
if header[:level] == header_level
|
||||||
|
header_to_nest[:children].push header if header_to_nest
|
||||||
|
true
|
||||||
|
else
|
||||||
|
header_to_nest = header if header[:level] < header_level
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
headers
|
||||||
|
end
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Unique header generation
|
||||||
|
require 'middleman-core/renderers/redcarpet'
|
||||||
|
require 'digest'
|
||||||
|
class UniqueHeadCounter < Middleman::Renderers::MiddlemanRedcarpetHTML
|
||||||
|
def initialize
|
||||||
|
super
|
||||||
|
@head_count = {}
|
||||||
|
end
|
||||||
|
def header(text, header_level)
|
||||||
|
friendly_text = text.gsub(/<[^>]*>/,"").parameterize
|
||||||
|
if friendly_text.strip.length == 0
|
||||||
|
# Looks like parameterize removed the whole thing! It removes many unicode
|
||||||
|
# characters like Chinese and Russian. To get a unique URL, let's just
|
||||||
|
# URI escape the whole header
|
||||||
|
friendly_text = Digest::SHA1.hexdigest(text)[0,10]
|
||||||
|
end
|
||||||
|
@head_count[friendly_text] ||= 0
|
||||||
|
@head_count[friendly_text] += 1
|
||||||
|
if @head_count[friendly_text] > 1
|
||||||
|
friendly_text += "-#{@head_count[friendly_text]}"
|
||||||
|
end
|
||||||
|
return "<h#{header_level} id='#{friendly_text}'>#{text}</h#{header_level}>"
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,248 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -o errexit #abort if any command fails
|
||||||
|
|
||||||
|
me=$(basename "$0")
|
||||||
|
|
||||||
|
help_message="\
|
||||||
|
Usage: $me [<options>] <command> [<command-options>]
|
||||||
|
Run commands related to the slate process.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
|
||||||
|
serve Run the middleman server process, useful for
|
||||||
|
development.
|
||||||
|
build Run the build process.
|
||||||
|
deploy Will build and deploy files to branch. Use
|
||||||
|
--no-build to only deploy.
|
||||||
|
|
||||||
|
Global Options:
|
||||||
|
|
||||||
|
-h, --help Show this help information.
|
||||||
|
-v, --verbose Increase verbosity. Useful for debugging.
|
||||||
|
|
||||||
|
Deploy options:
|
||||||
|
-e, --allow-empty Allow deployment of an empty directory.
|
||||||
|
-m, --message MESSAGE Specify the message used when committing on the
|
||||||
|
deploy branch.
|
||||||
|
-n, --no-hash Don't append the source commit's hash to the deploy
|
||||||
|
commit's message.
|
||||||
|
--no-build Do not build the source files.
|
||||||
|
"
|
||||||
|
|
||||||
|
|
||||||
|
run_serve() {
|
||||||
|
exec bundle exec middleman serve --watcher-force-polling
|
||||||
|
}
|
||||||
|
|
||||||
|
run_build() {
|
||||||
|
bundle exec middleman build --clean
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
# Set args from a local environment file.
|
||||||
|
if [ -e ".env" ]; then
|
||||||
|
source .env
|
||||||
|
fi
|
||||||
|
|
||||||
|
command=
|
||||||
|
|
||||||
|
# Parse arg flags
|
||||||
|
# If something is exposed as an environment variable, set/overwrite it
|
||||||
|
# here. Otherwise, set/overwrite the internal variable instead.
|
||||||
|
while : ; do
|
||||||
|
if [[ $1 = "-h" || $1 = "--help" ]]; then
|
||||||
|
echo "$help_message"
|
||||||
|
exit 0
|
||||||
|
elif [[ $1 = "-v" || $1 = "--verbose" ]]; then
|
||||||
|
verbose=true
|
||||||
|
shift
|
||||||
|
elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then
|
||||||
|
allow_empty=true
|
||||||
|
shift
|
||||||
|
elif [[ ( $1 = "-m" || $1 = "--message" ) && -n $2 ]]; then
|
||||||
|
commit_message=$2
|
||||||
|
shift 2
|
||||||
|
elif [[ $1 = "-n" || $1 = "--no-hash" ]]; then
|
||||||
|
GIT_DEPLOY_APPEND_HASH=false
|
||||||
|
shift
|
||||||
|
elif [[ $1 = "--no-build" ]]; then
|
||||||
|
no_build=true
|
||||||
|
shift
|
||||||
|
elif [[ $1 = "serve" || $1 = "build" || $1 = "deploy" ]]; then
|
||||||
|
if [ ! -z "${command}" ]; then
|
||||||
|
>&2 echo "You can only specify one command."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
command=$1
|
||||||
|
shift
|
||||||
|
elif [ -z $1 ]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "${command}" ]; then
|
||||||
|
>&2 echo "Command not specified."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set internal option vars from the environment and arg flags. All internal
|
||||||
|
# vars should be declared here, with sane defaults if applicable.
|
||||||
|
|
||||||
|
# Source directory & target branch.
|
||||||
|
deploy_directory=build
|
||||||
|
deploy_branch=gh-pages
|
||||||
|
|
||||||
|
#if no user identity is already set in the current git environment, use this:
|
||||||
|
default_username=${GIT_DEPLOY_USERNAME:-deploy.sh}
|
||||||
|
default_email=${GIT_DEPLOY_EMAIL:-}
|
||||||
|
|
||||||
|
#repository to deploy to. must be readable and writable.
|
||||||
|
repo=origin
|
||||||
|
|
||||||
|
#append commit hash to the end of message by default
|
||||||
|
append_hash=${GIT_DEPLOY_APPEND_HASH:-true}
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
enable_expanded_output
|
||||||
|
|
||||||
|
if ! git diff --exit-code --quiet --cached; then
|
||||||
|
echo Aborting due to uncommitted changes in the index >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
commit_title=`git log -n 1 --format="%s" HEAD`
|
||||||
|
commit_hash=` git log -n 1 --format="%H" HEAD`
|
||||||
|
|
||||||
|
#default commit message uses last title if a custom one is not supplied
|
||||||
|
if [[ -z $commit_message ]]; then
|
||||||
|
commit_message="publish: $commit_title"
|
||||||
|
fi
|
||||||
|
|
||||||
|
#append hash to commit message unless no hash flag was found
|
||||||
|
if [ $append_hash = true ]; then
|
||||||
|
commit_message="$commit_message"$'\n\n'"generated from commit $commit_hash"
|
||||||
|
fi
|
||||||
|
|
||||||
|
previous_branch=`git rev-parse --abbrev-ref HEAD`
|
||||||
|
|
||||||
|
if [ ! -d "$deploy_directory" ]; then
|
||||||
|
echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# must use short form of flag in ls for compatibility with macOS and BSD
|
||||||
|
if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then
|
||||||
|
echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the --allow-empty / -e flag." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git ls-remote --exit-code $repo "refs/heads/$deploy_branch" ; then
|
||||||
|
# deploy_branch exists in $repo; make sure we have the latest version
|
||||||
|
|
||||||
|
disable_expanded_output
|
||||||
|
git fetch --force $repo $deploy_branch:$deploy_branch
|
||||||
|
enable_expanded_output
|
||||||
|
fi
|
||||||
|
|
||||||
|
# check if deploy_branch exists locally
|
||||||
|
if git show-ref --verify --quiet "refs/heads/$deploy_branch"
|
||||||
|
then incremental_deploy
|
||||||
|
else initial_deploy
|
||||||
|
fi
|
||||||
|
|
||||||
|
restore_head
|
||||||
|
}
|
||||||
|
|
||||||
|
initial_deploy() {
|
||||||
|
git --work-tree "$deploy_directory" checkout --orphan $deploy_branch
|
||||||
|
git --work-tree "$deploy_directory" add --all
|
||||||
|
commit+push
|
||||||
|
}
|
||||||
|
|
||||||
|
incremental_deploy() {
|
||||||
|
#make deploy_branch the current branch
|
||||||
|
git symbolic-ref HEAD refs/heads/$deploy_branch
|
||||||
|
#put the previously committed contents of deploy_branch into the index
|
||||||
|
git --work-tree "$deploy_directory" reset --mixed --quiet
|
||||||
|
git --work-tree "$deploy_directory" add --all
|
||||||
|
|
||||||
|
set +o errexit
|
||||||
|
diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD --)$?
|
||||||
|
set -o errexit
|
||||||
|
case $diff in
|
||||||
|
0) echo No changes to files in $deploy_directory. Skipping commit.;;
|
||||||
|
1) commit+push;;
|
||||||
|
*)
|
||||||
|
echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to main, use: git symbolic-ref HEAD refs/heads/main && git reset --mixed >&2
|
||||||
|
return $diff
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
commit+push() {
|
||||||
|
set_user_id
|
||||||
|
git --work-tree "$deploy_directory" commit -m "$commit_message"
|
||||||
|
|
||||||
|
disable_expanded_output
|
||||||
|
#--quiet is important here to avoid outputting the repo URL, which may contain a secret token
|
||||||
|
git push --quiet $repo $deploy_branch
|
||||||
|
enable_expanded_output
|
||||||
|
}
|
||||||
|
|
||||||
|
#echo expanded commands as they are executed (for debugging)
|
||||||
|
enable_expanded_output() {
|
||||||
|
if [ $verbose ]; then
|
||||||
|
set -o xtrace
|
||||||
|
set +o verbose
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
#this is used to avoid outputting the repo URL, which may contain a secret token
|
||||||
|
disable_expanded_output() {
|
||||||
|
if [ $verbose ]; then
|
||||||
|
set +o xtrace
|
||||||
|
set -o verbose
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_user_id() {
|
||||||
|
if [[ -z `git config user.name` ]]; then
|
||||||
|
git config user.name "$default_username"
|
||||||
|
fi
|
||||||
|
if [[ -z `git config user.email` ]]; then
|
||||||
|
git config user.email "$default_email"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
restore_head() {
|
||||||
|
if [[ $previous_branch = "HEAD" ]]; then
|
||||||
|
#we weren't on any branch before, so just set HEAD back to the commit it was on
|
||||||
|
git update-ref --no-deref HEAD $commit_hash $deploy_branch
|
||||||
|
else
|
||||||
|
git symbolic-ref HEAD refs/heads/$previous_branch
|
||||||
|
fi
|
||||||
|
|
||||||
|
git reset --mixed
|
||||||
|
}
|
||||||
|
|
||||||
|
filter() {
|
||||||
|
sed -e "s|$repo|\$repo|g"
|
||||||
|
}
|
||||||
|
|
||||||
|
sanitize() {
|
||||||
|
"$@" 2> >(filter 1>&2) | filter
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
if [ "${command}" = "serve" ]; then
|
||||||
|
run_serve
|
||||||
|
elif [[ "${command}" = "build" ]]; then
|
||||||
|
run_build
|
||||||
|
elif [[ ${command} = "deploy" ]]; then
|
||||||
|
if [[ ${no_build} != true ]]; then
|
||||||
|
run_build
|
||||||
|
fi
|
||||||
|
main "$@"
|
||||||
|
fi
|
Binary file not shown.
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<metadata>Generated by IcoMoon</metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="slate" horiz-adv-x="1024">
|
||||||
|
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||||
|
<missing-glyph horiz-adv-x="1024" />
|
||||||
|
<glyph unicode=" " d="" horiz-adv-x="512" />
|
||||||
|
<glyph unicode="" d="M438.857 877.714q119.429 0 220.286-58.857t159.714-159.714 58.857-220.286-58.857-220.286-159.714-159.714-220.286-58.857-220.286 58.857-159.714 159.714-58.857 220.286 58.857 220.286 159.714 159.714 220.286 58.857zM512 165.143v108.571q0 8-5.143 13.429t-12.571 5.429h-109.714q-7.429 0-13.143-5.714t-5.714-13.143v-108.571q0-7.429 5.714-13.143t13.143-5.714h109.714q7.429 0 12.571 5.429t5.143 13.429zM510.857 361.714l10.286 354.857q0 6.857-5.714 10.286-5.714 4.571-13.714 4.571h-125.714q-8 0-13.714-4.571-5.714-3.429-5.714-10.286l9.714-354.857q0-5.714 5.714-10t13.714-4.286h105.714q8 0 13.429 4.286t6 10z" />
|
||||||
|
<glyph unicode="" d="M585.143 164.571v91.429q0 8-5.143 13.143t-13.143 5.143h-54.857v292.571q0 8-5.143 13.143t-13.143 5.143h-182.857q-8 0-13.143-5.143t-5.143-13.143v-91.429q0-8 5.143-13.143t13.143-5.143h54.857v-182.857h-54.857q-8 0-13.143-5.143t-5.143-13.143v-91.429q0-8 5.143-13.143t13.143-5.143h256q8 0 13.143 5.143t5.143 13.143zM512 676.571v91.429q0 8-5.143 13.143t-13.143 5.143h-109.714q-8 0-13.143-5.143t-5.143-13.143v-91.429q0-8 5.143-13.143t13.143-5.143h109.714q8 0 13.143 5.143t5.143 13.143zM877.714 438.857q0-119.429-58.857-220.286t-159.714-159.714-220.286-58.857-220.286 58.857-159.714 159.714-58.857 220.286 58.857 220.286 159.714 159.714 220.286 58.857 220.286-58.857 159.714-159.714 58.857-220.286z" />
|
||||||
|
<glyph unicode="" d="M733.714 531.428q0 16-10.286 26.286l-52 51.429q-10.857 10.857-25.714 10.857t-25.714-10.857l-233.143-232.571-129.143 129.143q-10.857 10.857-25.714 10.857t-25.714-10.857l-52-51.429q-10.286-10.286-10.286-26.286 0-15.429 10.286-25.714l206.857-206.857q10.857-10.857 25.714-10.857 15.429 0 26.286 10.857l310.286 310.286q10.286 10.286 10.286 25.714zM877.714 438.857q0-119.429-58.857-220.286t-159.714-159.714-220.286-58.857-220.286 58.857-159.714 159.714-58.857 220.286 58.857 220.286 159.714 159.714 220.286 58.857 220.286-58.857 159.714-159.714 58.857-220.286z" />
|
||||||
|
<glyph unicode="" d="M658.286 475.428q0 105.714-75.143 180.857t-180.857 75.143-180.857-75.143-75.143-180.857 75.143-180.857 180.857-75.143 180.857 75.143 75.143 180.857zM950.857 0q0-29.714-21.714-51.429t-51.429-21.714q-30.857 0-51.429 21.714l-196 195.429q-102.286-70.857-228-70.857-81.714 0-156.286 31.714t-128.571 85.714-85.714 128.571-31.714 156.286 31.714 156.286 85.714 128.571 128.571 85.714 156.286 31.714 156.286-31.714 128.571-85.714 85.714-128.571 31.714-156.286q0-125.714-70.857-228l196-196q21.143-21.143 21.143-51.429z" horiz-adv-x="951" />
|
||||||
|
</font></defs></svg>
|
After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 96 B |
|
@ -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
|
|
@ -0,0 +1,172 @@
|
||||||
|
# Build Logs
|
||||||
|
|
||||||
|
<aside class="notice">
|
||||||
|
|
||||||
|
All routes in this section require authentication.
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
Endpoints for interacting with stored build logs.
|
||||||
|
|
||||||
|
## List logs
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl \
|
||||||
|
-H 'X-Api-Key: secret' \
|
||||||
|
https://example.com/api/v1/logs?offset=10&limit=20
|
||||||
|
```
|
||||||
|
|
||||||
|
> JSON output format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"target_id": 3,
|
||||||
|
"start_time": 1652008554,
|
||||||
|
"end_time": 1652008559,
|
||||||
|
"arch": "x86_64",
|
||||||
|
"exit_code": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Retrieve a list of build logs.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`GET /api/v1/logs`
|
||||||
|
|
||||||
|
### Query Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
limit | Maximum amount of results to return.
|
||||||
|
offset | Offset of results.
|
||||||
|
target | Only return builds for this target id.
|
||||||
|
before | Only return logs started before this time (UTC epoch)
|
||||||
|
after | Only return logs started after this time (UTC epoch)
|
||||||
|
arch | Only return logs built on this architecture
|
||||||
|
exit_codes | Comma-separated list of exit codes to limit result to; using `!` as a prefix makes it exclude that value. For example, `1,2` only returns logs with status code 1 or 2, while `!1,!2` returns those that don't have 1 or 2 as the result.
|
||||||
|
|
||||||
|
|
||||||
|
## Get build log
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl \
|
||||||
|
-H 'X-Api-Key: secret' \
|
||||||
|
https://example.com/api/v1/logs/1
|
||||||
|
```
|
||||||
|
|
||||||
|
> JSON output format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"target_id": 3,
|
||||||
|
"start_time": 1652008554,
|
||||||
|
"end_time": 1652008559,
|
||||||
|
"arch": "x86_64",
|
||||||
|
"exit_code": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Retrieve info about a specific build log.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`GET /api/v1/logs/:id`
|
||||||
|
|
||||||
|
### URL Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
id | ID of requested log
|
||||||
|
|
||||||
|
## Get log contents
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl \
|
||||||
|
-H 'X-Api-Key: secret' \
|
||||||
|
https://example.com/api/v1/logs/15/content
|
||||||
|
```
|
||||||
|
|
||||||
|
Retrieve the contents of a build log. The response is the build log in
|
||||||
|
plaintext.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`GET /api/v1/logs/:id/content`
|
||||||
|
|
||||||
|
### URL Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
id | ID of requested log
|
||||||
|
|
||||||
|
## Publish build log
|
||||||
|
|
||||||
|
> JSON output format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"id": 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<aside class="warning">
|
||||||
|
|
||||||
|
This endpoint is used by the agents and should not be used manually unless you
|
||||||
|
know what you're doing. It's just here for completeness.
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
Publish a new build log to the server.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`POST /api/v1/logs`
|
||||||
|
|
||||||
|
### Query parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
startTime | Start time of the build (UTC epoch)
|
||||||
|
endTime | End time of the build (UTC epoch)
|
||||||
|
arch | Architecture on which the build was done
|
||||||
|
exitCode | Exit code of the build container
|
||||||
|
target | id of target this build is for
|
||||||
|
|
||||||
|
### Request body
|
||||||
|
|
||||||
|
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
|
|
@ -0,0 +1,179 @@
|
||||||
|
# Repository
|
||||||
|
|
||||||
|
Besides providing a RESTful API, the Vieter server is also a Pacman-compatible
|
||||||
|
repository server. This section describes the various routes that make this
|
||||||
|
possible.
|
||||||
|
|
||||||
|
## Get a package archive or database file
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -L https://example.com/bur/x86_64/tuxedo-keyboard-3.0.10-1-x86_64.pkg.tar.zst
|
||||||
|
```
|
||||||
|
|
||||||
|
This endpoint is really the entire repository. It serves both the package
|
||||||
|
archives & the database files for a specific arch-repo. It has three different
|
||||||
|
behaviors, depending on `filename`:
|
||||||
|
|
||||||
|
* If the file extension is one of `.db`, `.files`, `.db.tar.gz` or
|
||||||
|
`.files.tar.gz`, it tries to serve the requested database file.
|
||||||
|
* If the filename contains `.pkg`, it serves the package file.
|
||||||
|
* Otherwise, it assumes `filename` is the name & version of a package inside
|
||||||
|
the repository (e.g. `vieter-0.3.0_alpha.2-1`) & serves that package's `desc`
|
||||||
|
file from inside the database archive.
|
||||||
|
|
||||||
|
<aside class="notice">
|
||||||
|
|
||||||
|
The final option might sound a little strange, but it's used by the build
|
||||||
|
system to determine whether a package needs to be rebuilt.
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`GET /:repo/:arch/:filename`
|
||||||
|
|
||||||
|
### URL Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
repo | Repository containing the package
|
||||||
|
arch | Arch-repo containing the package
|
||||||
|
filename | actual filename to request
|
||||||
|
|
||||||
|
## Check whether file exists
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -L https://example.com/bur/x86_64/tuxedo-keyboard-3.0.10-1-x86_64.pkg.tar.zst
|
||||||
|
```
|
||||||
|
|
||||||
|
The above request can also be performed as a HEAD request. The behavior is the
|
||||||
|
same, except no data is returned besides an error 404 if the file doesn't exist
|
||||||
|
& an error 200 otherwise.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`GET /:repo/:arch/:filename`
|
||||||
|
|
||||||
|
### URL Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
repo | Repository containing the package
|
||||||
|
arch | Arch-repo containing the package
|
||||||
|
filename | actual filename to request
|
||||||
|
|
||||||
|
## Publish package
|
||||||
|
|
||||||
|
<aside class="notice">
|
||||||
|
|
||||||
|
This endpoint requests authentication.
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl \
|
||||||
|
-H 'X-Api-Key: secret' \
|
||||||
|
-XPOST \
|
||||||
|
-T tuxedo-keyboard-3.0.10-1-x86_64.pkg.tar.zst \
|
||||||
|
https://example.com/some-repo/publish
|
||||||
|
```
|
||||||
|
|
||||||
|
This endpoint allows you to publish a new package archive to a given repo.
|
||||||
|
|
||||||
|
If the package's architecture is not `any`, it is added to that specific
|
||||||
|
arch-repo. Otherwise, it is added to the configured default architecture & any
|
||||||
|
other already present arch-repos.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`POST /:repo/publish`
|
||||||
|
|
||||||
|
### URL Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
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
|
|
@ -0,0 +1,181 @@
|
||||||
|
# Targets
|
||||||
|
|
||||||
|
<aside class="notice">
|
||||||
|
|
||||||
|
All routes in this section require authentication.
|
||||||
|
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
Endpoints for interacting with the list of targets stored on the server.
|
||||||
|
|
||||||
|
## List targets
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl \
|
||||||
|
-H 'X-Api-Key: secret' \
|
||||||
|
https://example.com/api/v1/targets?offset=10&limit=20
|
||||||
|
```
|
||||||
|
|
||||||
|
> JSON output format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"kind": "git",
|
||||||
|
"url": "https://aur.archlinux.org/discord-ptb.git",
|
||||||
|
"branch": "master",
|
||||||
|
"path" : "",
|
||||||
|
"repo": "bur",
|
||||||
|
"schedule": "",
|
||||||
|
"arch": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"target_id": 1,
|
||||||
|
"value": "x86_64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Retrieve a list of targets.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`GET /api/v1/targets`
|
||||||
|
|
||||||
|
### Query Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
limit | Maximum amount of results to return.
|
||||||
|
offset | Offset of results.
|
||||||
|
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 specific target
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl \
|
||||||
|
-H 'X-Api-Key: secret' \
|
||||||
|
https://example.com/api/v1/targets/1
|
||||||
|
```
|
||||||
|
|
||||||
|
> JSON output format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"id": 1,
|
||||||
|
"kind": "git",
|
||||||
|
"url": "https://aur.archlinux.org/discord-ptb.git",
|
||||||
|
"branch": "master",
|
||||||
|
"path": "",
|
||||||
|
"repo": "bur",
|
||||||
|
"schedule": "0 2",
|
||||||
|
"arch": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"target_id": 1,
|
||||||
|
"value": "x86_64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Get info about a specific target.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`GET /api/v1/targets/:id`
|
||||||
|
|
||||||
|
### URL Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
id | id of requested target
|
||||||
|
|
||||||
|
## Create a new target
|
||||||
|
|
||||||
|
> JSON output format
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"message": "",
|
||||||
|
"data": {
|
||||||
|
"id": 15
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a new target with the given data.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`POST /api/v1/targets`
|
||||||
|
|
||||||
|
### Query Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
kind | Kind of target to add; one of 'git', 'url'.
|
||||||
|
url | URL 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.
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Modify a target
|
||||||
|
|
||||||
|
Modify the data of an existing target.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`PATCH /api/v1/targets/:id`
|
||||||
|
|
||||||
|
### URL Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
id | id of target to modify
|
||||||
|
|
||||||
|
### Query Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
kind | Kind of target; one of 'git', 'url'.
|
||||||
|
url | URL 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.
|
||||||
|
schedule | Cron build schedule
|
||||||
|
arch | Comma-separated list of architectures to build package on.
|
||||||
|
|
||||||
|
## Remove a target
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl \
|
||||||
|
-XDELETE \
|
||||||
|
-H 'X-Api-Key: secret' \
|
||||||
|
https://example.com/api/v1/targets/1
|
||||||
|
```
|
||||||
|
|
||||||
|
Remove a target from the server.
|
||||||
|
|
||||||
|
### HTTP Request
|
||||||
|
|
||||||
|
`DELETE /api/v1/targets/:id`
|
||||||
|
|
||||||
|
### URL Parameters
|
||||||
|
|
||||||
|
Parameter | Description
|
||||||
|
--------- | -----------
|
||||||
|
id | id of target to remove
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
title: API Reference
|
||||||
|
|
||||||
|
language_tabs: # must be one of https://git.io/vQNgJ
|
||||||
|
- shell: cURL
|
||||||
|
|
||||||
|
toc_footers:
|
||||||
|
- <a href='https://github.com/slatedocs/slate'>Documentation Powered by Slate</a>
|
||||||
|
|
||||||
|
includes:
|
||||||
|
- repository
|
||||||
|
- targets
|
||||||
|
- logs
|
||||||
|
- jobs
|
||||||
|
|
||||||
|
search: true
|
||||||
|
|
||||||
|
code_clipboard: true
|
||||||
|
|
||||||
|
meta:
|
||||||
|
- name: description
|
||||||
|
content: Documentation for the Vieter API
|
||||||
|
---
|
||||||
|
|
||||||
|
# Introduction
|
||||||
|
|
||||||
|
Welcome to the Vieter API documentation! Here, you can find everything related
|
||||||
|
to interacting with Vieter's HTTP API.
|
||||||
|
|
||||||
|
# Authentication
|
||||||
|
|
||||||
|
```shell
|
||||||
|
curl -H 'X-Api-Key: secret' https://example.com/api/some/path
|
||||||
|
```
|
||||||
|
|
||||||
|
> Don't forget to replace `secret` with your Vieter instance's secret.
|
||||||
|
|
||||||
|
Authentication is done by passing the HTTP header `X-Api-Key: secret` along
|
||||||
|
with each request, where `secret` is replaced with your Vieter server's
|
||||||
|
configured secret.
|
|
@ -0,0 +1,2 @@
|
||||||
|
//= require ./all_nosearch
|
||||||
|
//= require ./app/_search
|
|
@ -0,0 +1,27 @@
|
||||||
|
//= require ./lib/_energize
|
||||||
|
//= require ./app/_copy
|
||||||
|
//= require ./app/_toc
|
||||||
|
//= require ./app/_lang
|
||||||
|
|
||||||
|
function adjustLanguageSelectorWidth() {
|
||||||
|
const elem = $('.dark-box > .lang-selector');
|
||||||
|
elem.width(elem.parent().width());
|
||||||
|
}
|
||||||
|
|
||||||
|
$(function() {
|
||||||
|
loadToc($('#toc'), '.toc-link', '.toc-list-h2', 10);
|
||||||
|
setupLanguages($('body').data('languages'));
|
||||||
|
$('.content').imagesLoaded( function() {
|
||||||
|
window.recacheHeights();
|
||||||
|
window.refreshToc();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(window).resize(function() {
|
||||||
|
adjustLanguageSelectorWidth();
|
||||||
|
});
|
||||||
|
adjustLanguageSelectorWidth();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.onpopstate = function() {
|
||||||
|
activateLanguage(getLanguageFromQueryString());
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
function copyToClipboard(container) {
|
||||||
|
const el = document.createElement('textarea');
|
||||||
|
el.value = container.textContent.replace(/\n$/, '');
|
||||||
|
document.body.appendChild(el);
|
||||||
|
el.select();
|
||||||
|
document.execCommand('copy');
|
||||||
|
document.body.removeChild(el);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupCodeCopy() {
|
||||||
|
$('pre.highlight').prepend('<div class="copy-clipboard"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><title>Copy to Clipboard</title><path d="M18 6v-6h-18v18h6v6h18v-18h-6zm-12 10h-4v-14h14v4h-10v10zm16 6h-14v-14h14v14z"></path></svg></div>');
|
||||||
|
$('.copy-clipboard').on('click', function() {
|
||||||
|
copyToClipboard(this.parentNode.children[1]);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
//= require ../lib/_jquery
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2008-2013 Concur Technologies, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may obtain
|
||||||
|
a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
License for the specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
;(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var languages = [];
|
||||||
|
|
||||||
|
window.setupLanguages = setupLanguages;
|
||||||
|
window.activateLanguage = activateLanguage;
|
||||||
|
window.getLanguageFromQueryString = getLanguageFromQueryString;
|
||||||
|
|
||||||
|
function activateLanguage(language) {
|
||||||
|
if (!language) return;
|
||||||
|
if (language === "") return;
|
||||||
|
|
||||||
|
$(".lang-selector a").removeClass('active');
|
||||||
|
$(".lang-selector a[data-language-name='" + language + "']").addClass('active');
|
||||||
|
for (var i=0; i < languages.length; i++) {
|
||||||
|
$(".highlight.tab-" + languages[i]).hide();
|
||||||
|
$(".lang-specific." + languages[i]).hide();
|
||||||
|
}
|
||||||
|
$(".highlight.tab-" + language).show();
|
||||||
|
$(".lang-specific." + language).show();
|
||||||
|
|
||||||
|
window.recacheHeights();
|
||||||
|
|
||||||
|
// scroll to the new location of the position
|
||||||
|
if ($(window.location.hash).get(0)) {
|
||||||
|
$(window.location.hash).get(0).scrollIntoView(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseURL and stringifyURL are from https://github.com/sindresorhus/query-string
|
||||||
|
// MIT licensed
|
||||||
|
// https://github.com/sindresorhus/query-string/blob/7bee64c16f2da1a326579e96977b9227bf6da9e6/license
|
||||||
|
function parseURL(str) {
|
||||||
|
if (typeof str !== 'string') {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
str = str.trim().replace(/^(\?|#|&)/, '');
|
||||||
|
|
||||||
|
if (!str) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return str.split('&').reduce(function (ret, param) {
|
||||||
|
var parts = param.replace(/\+/g, ' ').split('=');
|
||||||
|
var key = parts[0];
|
||||||
|
var val = parts[1];
|
||||||
|
|
||||||
|
key = decodeURIComponent(key);
|
||||||
|
// missing `=` should be `null`:
|
||||||
|
// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
|
||||||
|
val = val === undefined ? null : decodeURIComponent(val);
|
||||||
|
|
||||||
|
if (!ret.hasOwnProperty(key)) {
|
||||||
|
ret[key] = val;
|
||||||
|
} else if (Array.isArray(ret[key])) {
|
||||||
|
ret[key].push(val);
|
||||||
|
} else {
|
||||||
|
ret[key] = [ret[key], val];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}, {});
|
||||||
|
};
|
||||||
|
|
||||||
|
function stringifyURL(obj) {
|
||||||
|
return obj ? Object.keys(obj).sort().map(function (key) {
|
||||||
|
var val = obj[key];
|
||||||
|
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
return val.sort().map(function (val2) {
|
||||||
|
return encodeURIComponent(key) + '=' + encodeURIComponent(val2);
|
||||||
|
}).join('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeURIComponent(key) + '=' + encodeURIComponent(val);
|
||||||
|
}).join('&') : '';
|
||||||
|
};
|
||||||
|
|
||||||
|
// gets the language set in the query string
|
||||||
|
function getLanguageFromQueryString() {
|
||||||
|
if (location.search.length >= 1) {
|
||||||
|
var language = parseURL(location.search).language;
|
||||||
|
if (language) {
|
||||||
|
return language;
|
||||||
|
} else if (jQuery.inArray(location.search.substr(1), languages) != -1) {
|
||||||
|
return location.search.substr(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns a new query string with the new language in it
|
||||||
|
function generateNewQueryString(language) {
|
||||||
|
var url = parseURL(location.search);
|
||||||
|
if (url.language) {
|
||||||
|
url.language = language;
|
||||||
|
return stringifyURL(url);
|
||||||
|
}
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if a button is clicked, add the state to the history
|
||||||
|
function pushURL(language) {
|
||||||
|
if (!history) { return; }
|
||||||
|
var hash = window.location.hash;
|
||||||
|
if (hash) {
|
||||||
|
hash = hash.replace(/^#+/, '');
|
||||||
|
}
|
||||||
|
history.pushState({}, '', '?' + generateNewQueryString(language) + '#' + hash);
|
||||||
|
|
||||||
|
// save language as next default
|
||||||
|
if (localStorage) {
|
||||||
|
localStorage.setItem("language", language);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupLanguages(l) {
|
||||||
|
var defaultLanguage = null;
|
||||||
|
if (localStorage) {
|
||||||
|
defaultLanguage = localStorage.getItem("language");
|
||||||
|
}
|
||||||
|
|
||||||
|
languages = l;
|
||||||
|
|
||||||
|
var presetLanguage = getLanguageFromQueryString();
|
||||||
|
if (presetLanguage) {
|
||||||
|
// the language is in the URL, so use that language!
|
||||||
|
activateLanguage(presetLanguage);
|
||||||
|
|
||||||
|
if (localStorage) {
|
||||||
|
localStorage.setItem("language", presetLanguage);
|
||||||
|
}
|
||||||
|
} else if ((defaultLanguage !== null) && (jQuery.inArray(defaultLanguage, languages) != -1)) {
|
||||||
|
// the language was the last selected one saved in localstorage, so use that language!
|
||||||
|
activateLanguage(defaultLanguage);
|
||||||
|
} else {
|
||||||
|
// no language selected, so use the default
|
||||||
|
activateLanguage(languages[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we click on a language tab, activate that language
|
||||||
|
$(function() {
|
||||||
|
$(".lang-selector a").on("click", function() {
|
||||||
|
var language = $(this).data("language-name");
|
||||||
|
pushURL(language);
|
||||||
|
activateLanguage(language);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
|
@ -0,0 +1,102 @@
|
||||||
|
//= require ../lib/_lunr
|
||||||
|
//= require ../lib/_jquery
|
||||||
|
//= require ../lib/_jquery.highlight
|
||||||
|
;(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var content, searchResults;
|
||||||
|
var highlightOpts = { element: 'span', className: 'search-highlight' };
|
||||||
|
var searchDelay = 0;
|
||||||
|
var timeoutHandle = 0;
|
||||||
|
var index;
|
||||||
|
|
||||||
|
function populate() {
|
||||||
|
index = lunr(function(){
|
||||||
|
|
||||||
|
this.ref('id');
|
||||||
|
this.field('title', { boost: 10 });
|
||||||
|
this.field('body');
|
||||||
|
this.pipeline.add(lunr.trimmer, lunr.stopWordFilter);
|
||||||
|
var lunrConfig = this;
|
||||||
|
|
||||||
|
$('h1, h2').each(function() {
|
||||||
|
var title = $(this);
|
||||||
|
var body = title.nextUntil('h1, h2');
|
||||||
|
lunrConfig.add({
|
||||||
|
id: title.prop('id'),
|
||||||
|
title: title.text(),
|
||||||
|
body: body.text()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
determineSearchDelay();
|
||||||
|
}
|
||||||
|
|
||||||
|
$(populate);
|
||||||
|
$(bind);
|
||||||
|
|
||||||
|
function determineSearchDelay() {
|
||||||
|
if (index.tokenSet.toArray().length>5000) {
|
||||||
|
searchDelay = 300;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function bind() {
|
||||||
|
content = $('.content');
|
||||||
|
searchResults = $('.search-results');
|
||||||
|
|
||||||
|
$('#input-search').on('keyup',function(e) {
|
||||||
|
var wait = function() {
|
||||||
|
return function(executingFunction, waitTime){
|
||||||
|
clearTimeout(timeoutHandle);
|
||||||
|
timeoutHandle = setTimeout(executingFunction, waitTime);
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
wait(function(){
|
||||||
|
search(e);
|
||||||
|
}, searchDelay);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function search(event) {
|
||||||
|
|
||||||
|
var searchInput = $('#input-search')[0];
|
||||||
|
|
||||||
|
unhighlight();
|
||||||
|
searchResults.addClass('visible');
|
||||||
|
|
||||||
|
// ESC clears the field
|
||||||
|
if (event.keyCode === 27) searchInput.value = '';
|
||||||
|
|
||||||
|
if (searchInput.value) {
|
||||||
|
var results = index.search(searchInput.value).filter(function(r) {
|
||||||
|
return r.score > 0.0001;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (results.length) {
|
||||||
|
searchResults.empty();
|
||||||
|
$.each(results, function (index, result) {
|
||||||
|
var elem = document.getElementById(result.ref);
|
||||||
|
searchResults.append("<li><a href='#" + result.ref + "'>" + $(elem).text() + "</a></li>");
|
||||||
|
});
|
||||||
|
highlight.call(searchInput);
|
||||||
|
} else {
|
||||||
|
searchResults.html('<li></li>');
|
||||||
|
$('.search-results li').text('No Results Found for "' + searchInput.value + '"');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unhighlight();
|
||||||
|
searchResults.removeClass('visible');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function highlight() {
|
||||||
|
if (this.value) content.highlight(this.value, highlightOpts);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unhighlight() {
|
||||||
|
content.unhighlight(highlightOpts);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
|
@ -0,0 +1,122 @@
|
||||||
|
//= require ../lib/_jquery
|
||||||
|
//= require ../lib/_imagesloaded.min
|
||||||
|
;(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var htmlPattern = /<[^>]*>/g;
|
||||||
|
var loaded = false;
|
||||||
|
|
||||||
|
var debounce = function(func, waitTime) {
|
||||||
|
var timeout = false;
|
||||||
|
return function() {
|
||||||
|
if (timeout === false) {
|
||||||
|
setTimeout(function() {
|
||||||
|
func();
|
||||||
|
timeout = false;
|
||||||
|
}, waitTime);
|
||||||
|
timeout = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var closeToc = function() {
|
||||||
|
$(".toc-wrapper").removeClass('open');
|
||||||
|
$("#nav-button").removeClass('open');
|
||||||
|
};
|
||||||
|
|
||||||
|
function loadToc($toc, tocLinkSelector, tocListSelector, scrollOffset) {
|
||||||
|
var headerHeights = {};
|
||||||
|
var pageHeight = 0;
|
||||||
|
var windowHeight = 0;
|
||||||
|
var originalTitle = document.title;
|
||||||
|
|
||||||
|
var recacheHeights = function() {
|
||||||
|
headerHeights = {};
|
||||||
|
pageHeight = $(document).height();
|
||||||
|
windowHeight = $(window).height();
|
||||||
|
|
||||||
|
$toc.find(tocLinkSelector).each(function() {
|
||||||
|
var targetId = $(this).attr('href');
|
||||||
|
if (targetId[0] === "#") {
|
||||||
|
headerHeights[targetId] = $("#" + $.escapeSelector(targetId.substring(1))).offset().top;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var refreshToc = function() {
|
||||||
|
var currentTop = $(document).scrollTop() + scrollOffset;
|
||||||
|
|
||||||
|
if (currentTop + windowHeight >= pageHeight) {
|
||||||
|
// at bottom of page, so just select last header by making currentTop very large
|
||||||
|
// this fixes the problem where the last header won't ever show as active if its content
|
||||||
|
// is shorter than the window height
|
||||||
|
currentTop = pageHeight + 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
var best = null;
|
||||||
|
for (var name in headerHeights) {
|
||||||
|
if ((headerHeights[name] < currentTop && headerHeights[name] > headerHeights[best]) || best === null) {
|
||||||
|
best = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catch the initial load case
|
||||||
|
if (currentTop == scrollOffset && !loaded) {
|
||||||
|
best = window.location.hash;
|
||||||
|
loaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $best = $toc.find("[href='" + best + "']").first();
|
||||||
|
if (!$best.hasClass("active")) {
|
||||||
|
// .active is applied to the ToC link we're currently on, and its parent <ul>s selected by tocListSelector
|
||||||
|
// .active-expanded is applied to the ToC links that are parents of this one
|
||||||
|
$toc.find(".active").removeClass("active");
|
||||||
|
$toc.find(".active-parent").removeClass("active-parent");
|
||||||
|
$best.addClass("active");
|
||||||
|
$best.parents(tocListSelector).addClass("active").siblings(tocLinkSelector).addClass('active-parent');
|
||||||
|
$best.siblings(tocListSelector).addClass("active");
|
||||||
|
$toc.find(tocListSelector).filter(":not(.active)").slideUp(150);
|
||||||
|
$toc.find(tocListSelector).filter(".active").slideDown(150);
|
||||||
|
if (window.history.replaceState) {
|
||||||
|
window.history.replaceState(null, "", best);
|
||||||
|
}
|
||||||
|
var thisTitle = $best.data("title");
|
||||||
|
if (thisTitle !== undefined && thisTitle.length > 0) {
|
||||||
|
document.title = thisTitle.replace(htmlPattern, "") + " – " + originalTitle;
|
||||||
|
} else {
|
||||||
|
document.title = originalTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var makeToc = function() {
|
||||||
|
recacheHeights();
|
||||||
|
refreshToc();
|
||||||
|
|
||||||
|
$("#nav-button").click(function() {
|
||||||
|
$(".toc-wrapper").toggleClass('open');
|
||||||
|
$("#nav-button").toggleClass('open');
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
$(".page-wrapper").click(closeToc);
|
||||||
|
$(".toc-link").click(closeToc);
|
||||||
|
|
||||||
|
// reload immediately after scrolling on toc click
|
||||||
|
$toc.find(tocLinkSelector).click(function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
refreshToc();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
$(window).scroll(debounce(refreshToc, 200));
|
||||||
|
$(window).resize(debounce(recacheHeights, 200));
|
||||||
|
};
|
||||||
|
|
||||||
|
makeToc();
|
||||||
|
|
||||||
|
window.recacheHeights = recacheHeights;
|
||||||
|
window.refreshToc = refreshToc;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.loadToc = loadToc;
|
||||||
|
})();
|
|
@ -0,0 +1,169 @@
|
||||||
|
/**
|
||||||
|
* energize.js v0.1.0
|
||||||
|
*
|
||||||
|
* Speeds up click events on mobile devices.
|
||||||
|
* https://github.com/davidcalhoun/energize.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() { // Sandbox
|
||||||
|
/**
|
||||||
|
* Don't add to non-touch devices, which don't need to be sped up
|
||||||
|
*/
|
||||||
|
if(!('ontouchstart' in window)) return;
|
||||||
|
|
||||||
|
var lastClick = {},
|
||||||
|
isThresholdReached, touchstart, touchmove, touchend,
|
||||||
|
click, closest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* isThresholdReached
|
||||||
|
*
|
||||||
|
* Compare touchstart with touchend xy coordinates,
|
||||||
|
* and only fire simulated click event if the coordinates
|
||||||
|
* are nearby. (don't want clicking to be confused with a swipe)
|
||||||
|
*/
|
||||||
|
isThresholdReached = function(startXY, xy) {
|
||||||
|
return Math.abs(startXY[0] - xy[0]) > 5 || Math.abs(startXY[1] - xy[1]) > 5;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* touchstart
|
||||||
|
*
|
||||||
|
* Save xy coordinates when the user starts touching the screen
|
||||||
|
*/
|
||||||
|
touchstart = function(e) {
|
||||||
|
this.startXY = [e.touches[0].clientX, e.touches[0].clientY];
|
||||||
|
this.threshold = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* touchmove
|
||||||
|
*
|
||||||
|
* Check if the user is scrolling past the threshold.
|
||||||
|
* Have to check here because touchend will not always fire
|
||||||
|
* on some tested devices (Kindle Fire?)
|
||||||
|
*/
|
||||||
|
touchmove = function(e) {
|
||||||
|
// NOOP if the threshold has already been reached
|
||||||
|
if(this.threshold) return false;
|
||||||
|
|
||||||
|
this.threshold = isThresholdReached(this.startXY, [e.touches[0].clientX, e.touches[0].clientY]);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* touchend
|
||||||
|
*
|
||||||
|
* If the user didn't scroll past the threshold between
|
||||||
|
* touchstart and touchend, fire a simulated click.
|
||||||
|
*
|
||||||
|
* (This will fire before a native click)
|
||||||
|
*/
|
||||||
|
touchend = function(e) {
|
||||||
|
// Don't fire a click if the user scrolled past the threshold
|
||||||
|
if(this.threshold || isThresholdReached(this.startXY, [e.changedTouches[0].clientX, e.changedTouches[0].clientY])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create and fire a click event on the target element
|
||||||
|
* https://developer.mozilla.org/en/DOM/event.initMouseEvent
|
||||||
|
*/
|
||||||
|
var touch = e.changedTouches[0],
|
||||||
|
evt = document.createEvent('MouseEvents');
|
||||||
|
evt.initMouseEvent('click', true, true, window, 0, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
|
||||||
|
evt.simulated = true; // distinguish from a normal (nonsimulated) click
|
||||||
|
e.target.dispatchEvent(evt);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* click
|
||||||
|
*
|
||||||
|
* Because we've already fired a click event in touchend,
|
||||||
|
* we need to listed for all native click events here
|
||||||
|
* and suppress them as necessary.
|
||||||
|
*/
|
||||||
|
click = function(e) {
|
||||||
|
/**
|
||||||
|
* Prevent ghost clicks by only allowing clicks we created
|
||||||
|
* in the click event we fired (look for e.simulated)
|
||||||
|
*/
|
||||||
|
var time = Date.now(),
|
||||||
|
timeDiff = time - lastClick.time,
|
||||||
|
x = e.clientX,
|
||||||
|
y = e.clientY,
|
||||||
|
xyDiff = [Math.abs(lastClick.x - x), Math.abs(lastClick.y - y)],
|
||||||
|
target = closest(e.target, 'A') || e.target, // needed for standalone apps
|
||||||
|
nodeName = target.nodeName,
|
||||||
|
isLink = nodeName === 'A',
|
||||||
|
standAlone = window.navigator.standalone && isLink && e.target.getAttribute("href");
|
||||||
|
|
||||||
|
lastClick.time = time;
|
||||||
|
lastClick.x = x;
|
||||||
|
lastClick.y = y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unfortunately Android sometimes fires click events without touch events (seen on Kindle Fire),
|
||||||
|
* so we have to add more logic to determine the time of the last click. Not perfect...
|
||||||
|
*
|
||||||
|
* Older, simpler check: if((!e.simulated) || standAlone)
|
||||||
|
*/
|
||||||
|
if((!e.simulated && (timeDiff < 500 || (timeDiff < 1500 && xyDiff[0] < 50 && xyDiff[1] < 50))) || standAlone) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if(!standAlone) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special logic for standalone web apps
|
||||||
|
* See http://stackoverflow.com/questions/2898740/iphone-safari-web-app-opens-links-in-new-window
|
||||||
|
*/
|
||||||
|
if(standAlone) {
|
||||||
|
window.location = target.getAttribute("href");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an energize-focus class to the targeted link (mimics :focus behavior)
|
||||||
|
* TODO: test and/or remove? Does this work?
|
||||||
|
*/
|
||||||
|
if(!target || !target.classList) return;
|
||||||
|
target.classList.add("energize-focus");
|
||||||
|
window.setTimeout(function(){
|
||||||
|
target.classList.remove("energize-focus");
|
||||||
|
}, 150);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* closest
|
||||||
|
* @param {HTMLElement} node current node to start searching from.
|
||||||
|
* @param {string} tagName the (uppercase) name of the tag you're looking for.
|
||||||
|
*
|
||||||
|
* Find the closest ancestor tag of a given node.
|
||||||
|
*
|
||||||
|
* Starts at node and goes up the DOM tree looking for a
|
||||||
|
* matching nodeName, continuing until hitting document.body
|
||||||
|
*/
|
||||||
|
closest = function(node, tagName){
|
||||||
|
var curNode = node;
|
||||||
|
|
||||||
|
while(curNode !== document.body) { // go up the dom until we find the tag we're after
|
||||||
|
if(!curNode || curNode.nodeName === tagName) { return curNode; } // found
|
||||||
|
curNode = curNode.parentNode; // not found, so keep going up
|
||||||
|
}
|
||||||
|
|
||||||
|
return null; // not found
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all delegated event listeners
|
||||||
|
*
|
||||||
|
* All the events we care about bubble up to document,
|
||||||
|
* so we can take advantage of event delegation.
|
||||||
|
*
|
||||||
|
* Note: no need to wait for DOMContentLoaded here
|
||||||
|
*/
|
||||||
|
document.addEventListener('touchstart', touchstart, false);
|
||||||
|
document.addEventListener('touchmove', touchmove, false);
|
||||||
|
document.addEventListener('touchend', touchend, false);
|
||||||
|
document.addEventListener('click', click, true); // TODO: why does this use capture?
|
||||||
|
|
||||||
|
})();
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* jQuery Highlight plugin
|
||||||
|
*
|
||||||
|
* Based on highlight v3 by Johann Burkard
|
||||||
|
* http://johannburkard.de/blog/programming/javascript/highlight-javascript-text-higlighting-jquery-plugin.html
|
||||||
|
*
|
||||||
|
* Code a little bit refactored and cleaned (in my humble opinion).
|
||||||
|
* Most important changes:
|
||||||
|
* - has an option to highlight only entire words (wordsOnly - false by default),
|
||||||
|
* - has an option to be case sensitive (caseSensitive - false by default)
|
||||||
|
* - highlight element tag and class names can be specified in options
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* // wrap every occurrance of text 'lorem' in content
|
||||||
|
* // with <span class='highlight'> (default options)
|
||||||
|
* $('#content').highlight('lorem');
|
||||||
|
*
|
||||||
|
* // search for and highlight more terms at once
|
||||||
|
* // so you can save some time on traversing DOM
|
||||||
|
* $('#content').highlight(['lorem', 'ipsum']);
|
||||||
|
* $('#content').highlight('lorem ipsum');
|
||||||
|
*
|
||||||
|
* // search only for entire word 'lorem'
|
||||||
|
* $('#content').highlight('lorem', { wordsOnly: true });
|
||||||
|
*
|
||||||
|
* // don't ignore case during search of term 'lorem'
|
||||||
|
* $('#content').highlight('lorem', { caseSensitive: true });
|
||||||
|
*
|
||||||
|
* // wrap every occurrance of term 'ipsum' in content
|
||||||
|
* // with <em class='important'>
|
||||||
|
* $('#content').highlight('ipsum', { element: 'em', className: 'important' });
|
||||||
|
*
|
||||||
|
* // remove default highlight
|
||||||
|
* $('#content').unhighlight();
|
||||||
|
*
|
||||||
|
* // remove custom highlight
|
||||||
|
* $('#content').unhighlight({ element: 'em', className: 'important' });
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 2009 Bartek Szopka
|
||||||
|
*
|
||||||
|
* Licensed under MIT license.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
jQuery.extend({
|
||||||
|
highlight: function (node, re, nodeName, className) {
|
||||||
|
if (node.nodeType === 3) {
|
||||||
|
var match = node.data.match(re);
|
||||||
|
if (match) {
|
||||||
|
var highlight = document.createElement(nodeName || 'span');
|
||||||
|
highlight.className = className || 'highlight';
|
||||||
|
var wordNode = node.splitText(match.index);
|
||||||
|
wordNode.splitText(match[0].length);
|
||||||
|
var wordClone = wordNode.cloneNode(true);
|
||||||
|
highlight.appendChild(wordClone);
|
||||||
|
wordNode.parentNode.replaceChild(highlight, wordNode);
|
||||||
|
return 1; //skip added node in parent
|
||||||
|
}
|
||||||
|
} else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
|
||||||
|
!/(script|style)/i.test(node.tagName) && // ignore script and style nodes
|
||||||
|
!(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
|
||||||
|
for (var i = 0; i < node.childNodes.length; i++) {
|
||||||
|
i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
jQuery.fn.unhighlight = function (options) {
|
||||||
|
var settings = { className: 'highlight', element: 'span' };
|
||||||
|
jQuery.extend(settings, options);
|
||||||
|
|
||||||
|
return this.find(settings.element + "." + settings.className).each(function () {
|
||||||
|
var parent = this.parentNode;
|
||||||
|
parent.replaceChild(this.firstChild, this);
|
||||||
|
parent.normalize();
|
||||||
|
}).end();
|
||||||
|
};
|
||||||
|
|
||||||
|
jQuery.fn.highlight = function (words, options) {
|
||||||
|
var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false };
|
||||||
|
jQuery.extend(settings, options);
|
||||||
|
|
||||||
|
if (words.constructor === String) {
|
||||||
|
words = [words];
|
||||||
|
}
|
||||||
|
words = jQuery.grep(words, function(word, i){
|
||||||
|
return word != '';
|
||||||
|
});
|
||||||
|
words = jQuery.map(words, function(word, i) {
|
||||||
|
return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
|
||||||
|
});
|
||||||
|
if (words.length == 0) { return this; };
|
||||||
|
|
||||||
|
var flag = settings.caseSensitive ? "" : "i";
|
||||||
|
var pattern = "(" + words.join("|") + ")";
|
||||||
|
if (settings.wordsOnly) {
|
||||||
|
pattern = "\\b" + pattern + "\\b";
|
||||||
|
}
|
||||||
|
var re = new RegExp(pattern, flag);
|
||||||
|
|
||||||
|
return this.each(function () {
|
||||||
|
jQuery.highlight(this, re, settings.element, settings.className);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,137 @@
|
||||||
|
<%#
|
||||||
|
Copyright 2008-2013 Concur Technologies, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may obtain
|
||||||
|
a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
License for the specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
%>
|
||||||
|
<% language_tabs = current_page.data.language_tabs || [] %>
|
||||||
|
<% page_content = yield %>
|
||||||
|
<%
|
||||||
|
if current_page.data.includes
|
||||||
|
current_page.data.includes.each do |include|
|
||||||
|
page_content += partial("includes/#{include}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
%>
|
||||||
|
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||||
|
<% if current_page.data.key?('meta') %>
|
||||||
|
<% current_page.data.meta.each do |meta| %>
|
||||||
|
<meta
|
||||||
|
<% meta.each do |key, value| %>
|
||||||
|
<%= "#{key}=\"#{value}\"" %>
|
||||||
|
<% end %>
|
||||||
|
>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<title><%= current_page.data.title || "API Documentation" %></title>
|
||||||
|
|
||||||
|
<style media="screen">
|
||||||
|
<%= Rouge::Themes::MonokaiSublimeSlate.render(:scope => '.highlight') %>
|
||||||
|
</style>
|
||||||
|
<style media="print">
|
||||||
|
* {
|
||||||
|
transition:none!important;
|
||||||
|
}
|
||||||
|
<%= Rouge::Themes::Base16::Solarized.render(:scope => '.highlight') %>
|
||||||
|
</style>
|
||||||
|
<%= stylesheet_link_tag :screen, media: :screen %>
|
||||||
|
<%= stylesheet_link_tag :print, media: :print %>
|
||||||
|
<% if current_page.data.search %>
|
||||||
|
<%= javascript_include_tag "all" %>
|
||||||
|
<% else %>
|
||||||
|
<%= javascript_include_tag "all_nosearch" %>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<% if current_page.data.code_clipboard %>
|
||||||
|
<script>
|
||||||
|
$(function() { setupCodeCopy(); });
|
||||||
|
</script>
|
||||||
|
<% end %>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="<%= page_classes %>" data-languages="<%=h language_tabs.map{ |lang| lang.is_a?(Hash) ? lang.keys.first : lang }.to_json %>">
|
||||||
|
<a href="#" id="nav-button">
|
||||||
|
<span>
|
||||||
|
NAV
|
||||||
|
<%= image_tag('navbar.png') %>
|
||||||
|
</span>
|
||||||
|
</a>
|
||||||
|
<div class="toc-wrapper">
|
||||||
|
<%= image_tag "logo.png", class: 'logo' %>
|
||||||
|
<% if language_tabs.any? %>
|
||||||
|
<div class="lang-selector">
|
||||||
|
<% language_tabs.each do |lang| %>
|
||||||
|
<% if lang.is_a? Hash %>
|
||||||
|
<a href="#" data-language-name="<%= lang.keys.first %>"><%= lang.values.first %></a>
|
||||||
|
<% else %>
|
||||||
|
<a href="#" data-language-name="<%= lang %>"><%= lang %></a>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
<% if current_page.data.search %>
|
||||||
|
<div class="search">
|
||||||
|
<input type="text" class="search" id="input-search" placeholder="Search">
|
||||||
|
</div>
|
||||||
|
<ul class="search-results"></ul>
|
||||||
|
<% end %>
|
||||||
|
<ul id="toc" class="toc-list-h1">
|
||||||
|
<% toc_data(page_content).each do |h1| %>
|
||||||
|
<li>
|
||||||
|
<a href="#<%= h1[:id] %>" class="toc-h1 toc-link" data-title="<%= h1[:title] %>"><%= h1[:content] %></a>
|
||||||
|
<% if h1[:children].length > 0 %>
|
||||||
|
<ul class="toc-list-h2">
|
||||||
|
<% h1[:children].each do |h2| %>
|
||||||
|
<li>
|
||||||
|
<a href="#<%= h2[:id] %>" class="toc-h2 toc-link" data-title="<%= h2[:title] %>"><%= h2[:content] %></a>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% if current_page.data.toc_footers %>
|
||||||
|
<ul class="toc-footer">
|
||||||
|
<% current_page.data.toc_footers.each do |footer| %>
|
||||||
|
<li><%= footer %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<div class="page-wrapper">
|
||||||
|
<div class="dark-box"></div>
|
||||||
|
<div class="content">
|
||||||
|
<%= page_content %>
|
||||||
|
</div>
|
||||||
|
<div class="dark-box">
|
||||||
|
<% if language_tabs.any? %>
|
||||||
|
<div class="lang-selector">
|
||||||
|
<% language_tabs.each do |lang| %>
|
||||||
|
<% if lang.is_a? Hash %>
|
||||||
|
<a href="#" data-language-name="<%= lang.keys.first %>"><%= lang.values.first %></a>
|
||||||
|
<% else %>
|
||||||
|
<a href="#" data-language-name="<%= lang %>"><%= lang %></a>
|
||||||
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,38 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'slate';
|
||||||
|
src:font-url('slate.eot?-syv14m');
|
||||||
|
src:font-url('slate.eot?#iefix-syv14m') format('embedded-opentype'),
|
||||||
|
font-url('slate.woff2?-syv14m') format('woff2'),
|
||||||
|
font-url('slate.woff?-syv14m') format('woff'),
|
||||||
|
font-url('slate.ttf?-syv14m') format('truetype'),
|
||||||
|
font-url('slate.svg?-syv14m#slate') format('svg');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
%icon {
|
||||||
|
font-family: 'slate';
|
||||||
|
speak: none;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
font-variant: normal;
|
||||||
|
text-transform: none;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
%icon-exclamation-sign {
|
||||||
|
@extend %icon;
|
||||||
|
content: "\e600";
|
||||||
|
}
|
||||||
|
%icon-info-sign {
|
||||||
|
@extend %icon;
|
||||||
|
content: "\e602";
|
||||||
|
}
|
||||||
|
%icon-ok-sign {
|
||||||
|
@extend %icon;
|
||||||
|
content: "\e606";
|
||||||
|
}
|
||||||
|
%icon-search {
|
||||||
|
@extend %icon;
|
||||||
|
content: "\e607";
|
||||||
|
}
|
|
@ -0,0 +1,427 @@
|
||||||
|
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Set default font family to sans-serif.
|
||||||
|
* 2. Prevent iOS text size adjust after orientation change, without disabling
|
||||||
|
* user zoom.
|
||||||
|
*/
|
||||||
|
|
||||||
|
html {
|
||||||
|
font-family: sans-serif; /* 1 */
|
||||||
|
-ms-text-size-adjust: 100%; /* 2 */
|
||||||
|
-webkit-text-size-adjust: 100%; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove default margin.
|
||||||
|
*/
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HTML5 display definitions
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct `block` display not defined for any HTML5 element in IE 8/9.
|
||||||
|
* Correct `block` display not defined for `details` or `summary` in IE 10/11
|
||||||
|
* and Firefox.
|
||||||
|
* Correct `block` display not defined for `main` in IE 11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
details,
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
main,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
section,
|
||||||
|
summary {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct `inline-block` display not defined in IE 8/9.
|
||||||
|
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio,
|
||||||
|
canvas,
|
||||||
|
progress,
|
||||||
|
video {
|
||||||
|
display: inline-block; /* 1 */
|
||||||
|
vertical-align: baseline; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent modern browsers from displaying `audio` without controls.
|
||||||
|
* Remove excess height in iOS 5 devices.
|
||||||
|
*/
|
||||||
|
|
||||||
|
audio:not([controls]) {
|
||||||
|
display: none;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address `[hidden]` styling not present in IE 8/9/10.
|
||||||
|
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
|
||||||
|
*/
|
||||||
|
|
||||||
|
[hidden],
|
||||||
|
template {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the gray background color from active links in IE 10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Improve readability when focused and also mouse hovered in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
a:active,
|
||||||
|
a:hover {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Text-level semantics
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
abbr[title] {
|
||||||
|
border-bottom: 1px dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
b,
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address styling not present in Safari and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
dfn {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address variable `h1` font-size and margin within `section` and `article`
|
||||||
|
* contexts in Firefox 4+, Safari, and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address styling not present in IE 8/9.
|
||||||
|
*/
|
||||||
|
|
||||||
|
mark {
|
||||||
|
background: #ff0;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address inconsistent and variable font size in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup {
|
||||||
|
top: -0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub {
|
||||||
|
bottom: -0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Embedded content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove border when inside `a` element in IE 8/9/10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
img {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Correct overflow not hidden in IE 9/10/11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
svg:not(:root) {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grouping content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address margin not present in IE 8/9 and Safari.
|
||||||
|
*/
|
||||||
|
|
||||||
|
figure {
|
||||||
|
margin: 1em 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address differences between Firefox and other browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
hr {
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
box-sizing: content-box;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Contain overflow in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address odd `em`-unit font size rendering in all browsers.
|
||||||
|
*/
|
||||||
|
|
||||||
|
code,
|
||||||
|
kbd,
|
||||||
|
pre,
|
||||||
|
samp {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Known limitation: by default, Chrome and Safari on OS X allow very limited
|
||||||
|
* styling of `select`, unless a `border` property is set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct color not being inherited.
|
||||||
|
* Known issue: affects color of disabled elements.
|
||||||
|
* 2. Correct font properties not being inherited.
|
||||||
|
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
optgroup,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
color: inherit; /* 1 */
|
||||||
|
font: inherit; /* 2 */
|
||||||
|
margin: 0; /* 3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address `overflow` set to `hidden` in IE 8/9/10/11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address inconsistent `text-transform` inheritance for `button` and `select`.
|
||||||
|
* All other form control elements do not inherit `text-transform` values.
|
||||||
|
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
|
||||||
|
* Correct `select` style inheritance in Firefox.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
select {
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
|
||||||
|
* and `video` controls.
|
||||||
|
* 2. Correct inability to style clickable `input` types in iOS.
|
||||||
|
* 3. Improve usability and consistency of cursor style between image-type
|
||||||
|
* `input` and others.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button,
|
||||||
|
html input[type="button"], /* 1 */
|
||||||
|
input[type="reset"],
|
||||||
|
input[type="submit"] {
|
||||||
|
-webkit-appearance: button; /* 2 */
|
||||||
|
cursor: pointer; /* 3 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Re-set default cursor for disabled elements.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button[disabled],
|
||||||
|
html input[disabled] {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove inner padding and border in Firefox 4+.
|
||||||
|
*/
|
||||||
|
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
input::-moz-focus-inner {
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
|
||||||
|
* the UA stylesheet.
|
||||||
|
*/
|
||||||
|
|
||||||
|
input {
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It's recommended that you don't attempt to style these elements.
|
||||||
|
* Firefox's implementation doesn't respect box-sizing, padding, or width.
|
||||||
|
*
|
||||||
|
* 1. Address box sizing set to `content-box` in IE 8/9/10.
|
||||||
|
* 2. Remove excess padding in IE 8/9/10.
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type="checkbox"],
|
||||||
|
input[type="radio"] {
|
||||||
|
box-sizing: border-box; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
|
||||||
|
* `font-size` values of the `input`, it causes the cursor style of the
|
||||||
|
* decrement button to change from `default` to `text`.
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type="number"]::-webkit-inner-spin-button,
|
||||||
|
input[type="number"]::-webkit-outer-spin-button {
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
|
||||||
|
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
|
||||||
|
* (include `-moz` to future-proof).
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type="search"] {
|
||||||
|
-webkit-appearance: textfield; /* 1 */
|
||||||
|
-moz-box-sizing: content-box;
|
||||||
|
-webkit-box-sizing: content-box; /* 2 */
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
|
||||||
|
* Safari (but not Chrome) clips the cancel button when the search input has
|
||||||
|
* padding (and `textfield` appearance).
|
||||||
|
*/
|
||||||
|
|
||||||
|
input[type="search"]::-webkit-search-cancel-button,
|
||||||
|
input[type="search"]::-webkit-search-decoration {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Define consistent border, margin, and padding.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 1px solid #c0c0c0;
|
||||||
|
margin: 0 2px;
|
||||||
|
padding: 0.35em 0.625em 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 1. Correct `color` not being inherited in IE 8/9/10/11.
|
||||||
|
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
legend {
|
||||||
|
border: 0; /* 1 */
|
||||||
|
padding: 0; /* 2 */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove default vertical scrollbar in IE 8/9/10/11.
|
||||||
|
*/
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Don't inherit the `font-weight` (applied by a rule above).
|
||||||
|
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
|
||||||
|
*/
|
||||||
|
|
||||||
|
optgroup {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove most spacing between table cells.
|
||||||
|
*/
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
td,
|
||||||
|
th {
|
||||||
|
padding: 0;
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// RTL Styles Variables
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
$default: auto;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TABLE OF CONTENTS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#toc>ul>li>a>span {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-wrapper {
|
||||||
|
transition: right 0.3s ease-in-out !important;
|
||||||
|
left: $default !important;
|
||||||
|
#{right}: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-h2 {
|
||||||
|
padding-#{right}: $nav-padding + $nav-indent;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-button {
|
||||||
|
#{right}: 0;
|
||||||
|
transition: right 0.3s ease-in-out;
|
||||||
|
&.open {
|
||||||
|
right: $nav-width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// PAGE LAYOUT AND CODE SAMPLE BACKGROUND
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
.page-wrapper {
|
||||||
|
margin-#{left}: $default !important;
|
||||||
|
margin-#{right}: $nav-width;
|
||||||
|
.dark-box {
|
||||||
|
#{right}: $default;
|
||||||
|
#{left}: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-selector {
|
||||||
|
width: $default !important;
|
||||||
|
a {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CODE SAMPLE STYLES
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
.content {
|
||||||
|
&>h1,
|
||||||
|
&>h2,
|
||||||
|
&>h3,
|
||||||
|
&>h4,
|
||||||
|
&>h5,
|
||||||
|
&>h6,
|
||||||
|
&>p,
|
||||||
|
&>table,
|
||||||
|
&>ul,
|
||||||
|
&>ol,
|
||||||
|
&>aside,
|
||||||
|
&>dl {
|
||||||
|
margin-#{left}: $examples-width;
|
||||||
|
margin-#{right}: $default !important;
|
||||||
|
}
|
||||||
|
&>ul,
|
||||||
|
&>ol {
|
||||||
|
padding-#{right}: $main-padding + 15px;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dd {
|
||||||
|
margin-#{right}: 15px;
|
||||||
|
}
|
||||||
|
aside {
|
||||||
|
aside:before {
|
||||||
|
padding-#{left}: 0.5em;
|
||||||
|
}
|
||||||
|
.search-highlight {
|
||||||
|
background: linear-gradient(to top right, #F7E633 0%, #F1D32F 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pre,
|
||||||
|
blockquote {
|
||||||
|
float: left !important;
|
||||||
|
clear: left !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TYPOGRAPHY
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
p,
|
||||||
|
aside {
|
||||||
|
text-align: right;
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-wrapper {
|
||||||
|
text-align: right;
|
||||||
|
direction: rtl;
|
||||||
|
font-weight: 100 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// RESPONSIVE DESIGN
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@media (max-width: $tablet-width) {
|
||||||
|
.toc-wrapper {
|
||||||
|
#{right}: -$nav-width;
|
||||||
|
&.open {
|
||||||
|
#{right}: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.page-wrapper {
|
||||||
|
margin-#{right}: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $phone-width) {
|
||||||
|
%left-col {
|
||||||
|
margin-#{left}: 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
Copyright 2008-2013 Concur Technologies, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may obtain
|
||||||
|
a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
License for the specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CUSTOMIZE SLATE
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Use these settings to help adjust the appearance of Slate
|
||||||
|
|
||||||
|
|
||||||
|
// BACKGROUND COLORS
|
||||||
|
////////////////////
|
||||||
|
$nav-bg: #2E3336 !default;
|
||||||
|
$examples-bg: #2E3336 !default;
|
||||||
|
$code-bg: #1E2224 !default;
|
||||||
|
$code-annotation-bg: #191D1F !default;
|
||||||
|
$nav-subitem-bg: #1E2224 !default;
|
||||||
|
$nav-active-bg: #0F75D4 !default;
|
||||||
|
$nav-active-parent-bg: #1E2224 !default; // parent links of the current section
|
||||||
|
$lang-select-border: #000 !default;
|
||||||
|
$lang-select-bg: #1E2224 !default;
|
||||||
|
$lang-select-active-bg: $examples-bg !default; // feel free to change this to blue or something
|
||||||
|
$lang-select-pressed-bg: #111 !default; // color of language tab bg when mouse is pressed
|
||||||
|
$main-bg: #F3F7F9 !default;
|
||||||
|
$aside-notice-bg: #8fbcd4 !default;
|
||||||
|
$aside-warning-bg: #c97a7e !default;
|
||||||
|
$aside-success-bg: #6ac174 !default;
|
||||||
|
$search-notice-bg: #c97a7e !default;
|
||||||
|
|
||||||
|
|
||||||
|
// TEXT COLORS
|
||||||
|
////////////////////
|
||||||
|
$main-text: #333 !default; // main content text color
|
||||||
|
$nav-text: #fff !default;
|
||||||
|
$nav-active-text: #fff !default;
|
||||||
|
$nav-active-parent-text: #fff !default; // parent links of the current section
|
||||||
|
$lang-select-text: #fff !default; // color of unselected language tab text
|
||||||
|
$lang-select-active-text: #fff !default; // color of selected language tab text
|
||||||
|
$lang-select-pressed-text: #fff !default; // color of language tab text when mouse is pressed
|
||||||
|
|
||||||
|
|
||||||
|
// SIZES
|
||||||
|
////////////////////
|
||||||
|
$nav-width: 230px !default; // width of the navbar
|
||||||
|
$examples-width: 50% !default; // portion of the screen taken up by code examples
|
||||||
|
$logo-margin: 0px !default; // margin below logo
|
||||||
|
$main-padding: 28px !default; // padding to left and right of content & examples
|
||||||
|
$nav-padding: 15px !default; // padding to left and right of navbar
|
||||||
|
$nav-v-padding: 10px !default; // padding used vertically around search boxes and results
|
||||||
|
$nav-indent: 10px !default; // extra padding for ToC subitems
|
||||||
|
$code-annotation-padding: 13px !default; // padding inside code annotations
|
||||||
|
$h1-margin-bottom: 21px !default; // padding under the largest header tags
|
||||||
|
$tablet-width: 930px !default; // min width before reverting to tablet size
|
||||||
|
$phone-width: $tablet-width - $nav-width !default; // min width before reverting to mobile size
|
||||||
|
|
||||||
|
|
||||||
|
// FONTS
|
||||||
|
////////////////////
|
||||||
|
%default-font {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
%header-font {
|
||||||
|
@extend %default-font;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
%code-font {
|
||||||
|
font-family: Consolas, Menlo, Monaco, "Lucida Console", "Liberation Mono", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Courier New", monospace, serif;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// OTHER
|
||||||
|
////////////////////
|
||||||
|
$nav-footer-border-color: #666 !default;
|
||||||
|
$search-box-border-color: #666 !default;
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// INTERNAL
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// These settings are probably best left alone.
|
||||||
|
|
||||||
|
%break-words {
|
||||||
|
word-break: break-all;
|
||||||
|
hyphens: auto;
|
||||||
|
}
|
|
@ -0,0 +1,153 @@
|
||||||
|
@charset "utf-8";
|
||||||
|
@import 'normalize';
|
||||||
|
@import 'variables';
|
||||||
|
@import 'icon-font';
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2008-2013 Concur Technologies, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may obtain
|
||||||
|
a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
License for the specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
$print-color: #999;
|
||||||
|
$print-color-light: #ccc;
|
||||||
|
$print-font-size: 12px;
|
||||||
|
|
||||||
|
body {
|
||||||
|
@extend %default-font;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tocify, .toc-footer, .lang-selector, .search, #nav-button {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tocify-wrapper>img {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
font-size: 12px;
|
||||||
|
|
||||||
|
pre, code {
|
||||||
|
@extend %code-font;
|
||||||
|
@extend %break-words;
|
||||||
|
border: 1px solid $print-color;
|
||||||
|
border-radius: 5px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
code {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
padding: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
padding: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border: 1px solid $print-color;
|
||||||
|
tr {
|
||||||
|
border-bottom: 1px solid $print-color;
|
||||||
|
}
|
||||||
|
td,th {
|
||||||
|
padding: 0.7em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@extend %header-font;
|
||||||
|
font-size: 2.5em;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
margin-top: 1em;
|
||||||
|
margin-bottom: $h1-margin-bottom;
|
||||||
|
border: 2px solid $print-color-light;
|
||||||
|
border-width: 2px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
@extend %header-font;
|
||||||
|
font-size: 1.8em;
|
||||||
|
margin-top: 2em;
|
||||||
|
border-top: 2px solid $print-color-light;
|
||||||
|
padding-top: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1+h2, h1+div+h2 {
|
||||||
|
border-top: none;
|
||||||
|
padding-top: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3, h4 {
|
||||||
|
@extend %header-font;
|
||||||
|
font-size: 0.8em;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
margin-bottom: 0.8em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5, h6 {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
padding: 1em;
|
||||||
|
border: 1px solid $print-color-light;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside:before {
|
||||||
|
vertical-align: middle;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.notice:before {
|
||||||
|
@extend %icon-info-sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.warning:before {
|
||||||
|
@extend %icon-exclamation-sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.success:before {
|
||||||
|
@extend %icon-ok-sign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-clipboard {
|
||||||
|
@media print {
|
||||||
|
display: none
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,633 @@
|
||||||
|
@charset "utf-8";
|
||||||
|
@import 'normalize';
|
||||||
|
@import 'variables';
|
||||||
|
@import 'icon-font';
|
||||||
|
// @import 'rtl'; // uncomment to switch to RTL format
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2008-2013 Concur Technologies, Inc.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may obtain
|
||||||
|
a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
License for the specific language governing permissions and limitations
|
||||||
|
under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GENERAL STUFF
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
color: $main-text;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
@extend %default-font;
|
||||||
|
background-color: $main-bg;
|
||||||
|
height: 100%;
|
||||||
|
-webkit-text-size-adjust: none; /* Never autoresize text */
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// TABLE OF CONTENTS
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#toc > ul > li > a > span {
|
||||||
|
float: right;
|
||||||
|
background-color: #2484FF;
|
||||||
|
border-radius: 40px;
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-wrapper {
|
||||||
|
transition: left 0.3s ease-in-out;
|
||||||
|
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 30;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
width: $nav-width;
|
||||||
|
background-color: $nav-bg;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
// language selector for mobile devices
|
||||||
|
.lang-selector {
|
||||||
|
display: none;
|
||||||
|
a {
|
||||||
|
padding-top: 0.5em;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the logo at the top of the ToC
|
||||||
|
.logo {
|
||||||
|
display: block;
|
||||||
|
max-width: 100%;
|
||||||
|
margin-bottom: $logo-margin;
|
||||||
|
}
|
||||||
|
|
||||||
|
&>.search {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input {
|
||||||
|
background: $nav-bg;
|
||||||
|
border-width: 0 0 1px 0;
|
||||||
|
border-color: $search-box-border-color;
|
||||||
|
padding: 6px 0 6px 20px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: $nav-v-padding $nav-padding;
|
||||||
|
width: $nav-width - ($nav-padding*2);
|
||||||
|
outline: none;
|
||||||
|
color: $nav-text;
|
||||||
|
border-radius: 0; /* ios has a default border radius */
|
||||||
|
}
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
position: absolute;
|
||||||
|
top: 17px;
|
||||||
|
left: $nav-padding;
|
||||||
|
color: $nav-text;
|
||||||
|
@extend %icon-search;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-results {
|
||||||
|
margin-top: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
transition-property: height, margin;
|
||||||
|
transition-duration: 180ms;
|
||||||
|
transition-timing-function: ease-in-out;
|
||||||
|
background: $nav-subitem-bg;
|
||||||
|
&.visible {
|
||||||
|
height: 30%;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin: 1em $nav-padding;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $nav-text;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The Table of Contents is composed of multiple nested
|
||||||
|
// unordered lists. These styles remove the default
|
||||||
|
// styling of an unordered list because it is ugly.
|
||||||
|
ul, li {
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
color: $nav-text;
|
||||||
|
transition-property: background;
|
||||||
|
transition-timing-function: linear;
|
||||||
|
transition-duration: 200ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the currently selected ToC entry
|
||||||
|
.toc-link.active {
|
||||||
|
background-color: $nav-active-bg;
|
||||||
|
color: $nav-active-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is parent links of the currently selected ToC entry
|
||||||
|
.toc-link.active-parent {
|
||||||
|
background-color: $nav-active-parent-bg;
|
||||||
|
color: $nav-active-parent-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-list-h2 {
|
||||||
|
display: none;
|
||||||
|
background-color: $nav-subitem-bg;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-h2 {
|
||||||
|
padding-left: $nav-padding + $nav-indent;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-footer {
|
||||||
|
padding: 1em 0;
|
||||||
|
margin-top: 1em;
|
||||||
|
border-top: 1px dashed $nav-footer-border-color;
|
||||||
|
|
||||||
|
li,a {
|
||||||
|
color: $nav-text;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
font-size: 0.8em;
|
||||||
|
line-height: 1.7;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link, .toc-footer li {
|
||||||
|
padding: 0 $nav-padding 0 $nav-padding;
|
||||||
|
display: block;
|
||||||
|
overflow-x: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
text-decoration: none;
|
||||||
|
color: $nav-text;
|
||||||
|
transition-property: background;
|
||||||
|
transition-timing-function: linear;
|
||||||
|
transition-duration: 130ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
// button to show navigation on mobile devices
|
||||||
|
#nav-button {
|
||||||
|
span {
|
||||||
|
display: block;
|
||||||
|
$side-pad: $main-padding / 2 - 8px;
|
||||||
|
padding: $side-pad $side-pad $side-pad;
|
||||||
|
background-color: rgba($main-bg, 0.7);
|
||||||
|
transform-origin: 0 0;
|
||||||
|
transform: rotate(-90deg) translate(-100%, 0);
|
||||||
|
border-radius: 0 0 0 5px;
|
||||||
|
}
|
||||||
|
padding: 0 1.5em 5em 0; // increase touch size area
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 100;
|
||||||
|
color: #000;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
opacity: 0.7;
|
||||||
|
line-height: 16px;
|
||||||
|
img {
|
||||||
|
height: 16px;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
transition: left 0.3s ease-in-out;
|
||||||
|
|
||||||
|
&:hover { opacity: 1; }
|
||||||
|
&.open {left: $nav-width}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// PAGE LAYOUT AND CODE SAMPLE BACKGROUND
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
.page-wrapper {
|
||||||
|
margin-left: $nav-width;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
background-color: $main-bg;
|
||||||
|
min-height: 100%;
|
||||||
|
|
||||||
|
padding-bottom: 1px; // prevent margin overflow
|
||||||
|
|
||||||
|
// The dark box is what gives the code samples their dark background.
|
||||||
|
// It sits essentially under the actual content block, which has a
|
||||||
|
// transparent background.
|
||||||
|
// I know, it's hackish, but it's the simplist way to make the left
|
||||||
|
// half of the content always this background color.
|
||||||
|
.dark-box {
|
||||||
|
width: $examples-width;
|
||||||
|
background-color: $examples-bg;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-selector {
|
||||||
|
position: fixed;
|
||||||
|
z-index: 50;
|
||||||
|
border-bottom: 5px solid $lang-select-active-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-selector {
|
||||||
|
display: flex;
|
||||||
|
background-color: $lang-select-bg;
|
||||||
|
width: 100%;
|
||||||
|
font-weight: bold;
|
||||||
|
overflow-x: auto;
|
||||||
|
a {
|
||||||
|
display: inline;
|
||||||
|
color: $lang-select-text;
|
||||||
|
text-decoration: none;
|
||||||
|
padding: 0 10px;
|
||||||
|
line-height: 30px;
|
||||||
|
outline: 0;
|
||||||
|
|
||||||
|
&:active, &:focus {
|
||||||
|
background-color: $lang-select-pressed-bg;
|
||||||
|
color: $lang-select-pressed-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: $lang-select-active-bg;
|
||||||
|
color: $lang-select-active-text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
clear: both;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CONTENT STYLES
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// This is all the stuff with the light background in the left half of the page
|
||||||
|
|
||||||
|
.content {
|
||||||
|
// fixes webkit rendering bug for some: see #538
|
||||||
|
-webkit-transform: translateZ(0);
|
||||||
|
// to place content above the dark box
|
||||||
|
position: relative;
|
||||||
|
z-index: 30;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
&>h1, &>h2, &>h3, &>h4, &>h5, &>h6, &>p, &>table, &>ul, &>ol, &>aside, &>dl {
|
||||||
|
margin-right: $examples-width;
|
||||||
|
padding: 0 $main-padding;
|
||||||
|
box-sizing: border-box;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
@extend %left-col;
|
||||||
|
}
|
||||||
|
|
||||||
|
&>ul, &>ol {
|
||||||
|
padding-left: $main-padding + 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the div is the tocify hidden div for placeholding stuff
|
||||||
|
&>h1, &>h2, &>div {
|
||||||
|
clear:both;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
@extend %header-font;
|
||||||
|
font-size: 25px;
|
||||||
|
padding-top: 0.5em;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
margin-bottom: $h1-margin-bottom;
|
||||||
|
margin-top: 2em;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
background-color: #fdfdfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1:first-child, div:first-child + h1 {
|
||||||
|
border-top-width: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
@extend %header-font;
|
||||||
|
font-size: 19px;
|
||||||
|
margin-top: 4em;
|
||||||
|
margin-bottom: 0;
|
||||||
|
border-top: 1px solid #ccc;
|
||||||
|
padding-top: 1.2em;
|
||||||
|
padding-bottom: 1.2em;
|
||||||
|
background-image: linear-gradient(to bottom, rgba(#fff, 0.2), rgba(#fff, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// h2s right after h1s should bump right up
|
||||||
|
// against the h1s.
|
||||||
|
h1 + h2, h1 + div + h2 {
|
||||||
|
margin-top: $h1-margin-bottom * -1;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3, h4, h5, h6 {
|
||||||
|
@extend %header-font;
|
||||||
|
font-size: 15px;
|
||||||
|
margin-top: 2.5em;
|
||||||
|
margin-bottom: 0.8em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4, h5, h6 {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 2em 0;
|
||||||
|
border-top: 2px solid $examples-bg;
|
||||||
|
border-bottom: 2px solid $main-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
overflow: auto;
|
||||||
|
th,td {
|
||||||
|
text-align: left;
|
||||||
|
vertical-align: top;
|
||||||
|
line-height: 1.6;
|
||||||
|
code {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
vertical-align: bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:last-child {
|
||||||
|
border-bottom: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(odd)>td {
|
||||||
|
background-color: lighten($main-bg,4.2%);
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even)>td {
|
||||||
|
background-color: lighten($main-bg,2.4%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p, li, dt, dd {
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: rgba(0,0,0,0.05);
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
@extend %break-words;
|
||||||
|
@extend %code-font;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre>code {
|
||||||
|
background-color: transparent;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside {
|
||||||
|
padding-top: 1em;
|
||||||
|
padding-bottom: 1em;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
background: $aside-notice-bg;
|
||||||
|
line-height: 1.6;
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background-color: $aside-warning-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: $aside-success-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aside:before {
|
||||||
|
vertical-align: middle;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.notice:before {
|
||||||
|
@extend %icon-info-sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.warning:before {
|
||||||
|
@extend %icon-exclamation-sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
aside.success:before {
|
||||||
|
@extend %icon-ok-sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-highlight {
|
||||||
|
padding: 2px;
|
||||||
|
margin: -3px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #F7E633;
|
||||||
|
background: linear-gradient(to top left, #F7E633 0%, #F1D32F 100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CODE SAMPLE STYLES
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// This is all the stuff that appears in the right half of the page
|
||||||
|
|
||||||
|
.content {
|
||||||
|
&>div.highlight {
|
||||||
|
clear:none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre, blockquote {
|
||||||
|
background-color: $code-bg;
|
||||||
|
color: #fff;
|
||||||
|
|
||||||
|
margin: 0;
|
||||||
|
width: $examples-width;
|
||||||
|
|
||||||
|
float:right;
|
||||||
|
clear:right;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
@extend %right-col;
|
||||||
|
|
||||||
|
&>p { margin: 0; }
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #fff;
|
||||||
|
text-decoration: none;
|
||||||
|
border-bottom: dashed 1px #ccc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
@extend %code-font;
|
||||||
|
padding-top: 2em;
|
||||||
|
padding-bottom: 2em;
|
||||||
|
padding: 2em $main-padding;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
&>p {
|
||||||
|
background-color: $code-annotation-bg;
|
||||||
|
padding: $code-annotation-padding 2em;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// RESPONSIVE DESIGN
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// These are the styles for phones and tablets
|
||||||
|
// There are also a couple styles disperesed
|
||||||
|
|
||||||
|
@media (max-width: $tablet-width) {
|
||||||
|
.toc-wrapper {
|
||||||
|
left: -$nav-width;
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-wrapper {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-link {
|
||||||
|
padding-top: 0.3em;
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $phone-width) {
|
||||||
|
.dark-box {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
%left-col {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toc-wrapper .lang-selector {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-wrapper .lang-selector {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
%right-col {
|
||||||
|
width: auto;
|
||||||
|
float: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
%right-col + %left-col {
|
||||||
|
margin-top: $main-padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight .c, .highlight .cm, .highlight .c1, .highlight .cs {
|
||||||
|
color: #909090;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight, .highlight .w {
|
||||||
|
background-color: $code-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-clipboard {
|
||||||
|
float: right;
|
||||||
|
fill: #9DAAB6;
|
||||||
|
cursor: pointer;
|
||||||
|
opacity: 0.4;
|
||||||
|
height: 18px;
|
||||||
|
width: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-clipboard:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
---
|
||||||
|
title: "{{ replace .Name "-" " " | title }}"
|
||||||
|
date: {{ .Date }}
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
|
@ -0,0 +1,115 @@
|
||||||
|
# hugo server --minify --themesDir ... --baseURL=http://0.0.0.0:1313/theme/hugo-book/
|
||||||
|
|
||||||
|
baseURL = 'https://rustybever.be/docs/vieter/'
|
||||||
|
title = 'Vieter - Docs'
|
||||||
|
theme = 'hugo-book'
|
||||||
|
|
||||||
|
# Book configuration
|
||||||
|
disablePathToLower = true
|
||||||
|
# Doesn't work with docs as subdir
|
||||||
|
enableGitInfo = true
|
||||||
|
|
||||||
|
# Needed for mermaid/katex shortcodes
|
||||||
|
[markup]
|
||||||
|
[markup.goldmark.renderer]
|
||||||
|
unsafe = true
|
||||||
|
|
||||||
|
[markup.tableOfContents]
|
||||||
|
startLevel = 1
|
||||||
|
|
||||||
|
# Multi-lingual mode config
|
||||||
|
# There are different options to translate files
|
||||||
|
# See https://gohugo.io/content-management/multilingual/#translation-by-filename
|
||||||
|
# And https://gohugo.io/content-management/multilingual/#translation-by-content-directory
|
||||||
|
[languages]
|
||||||
|
[languages.en]
|
||||||
|
languageName = 'English'
|
||||||
|
contentDir = 'content'
|
||||||
|
weight = 1
|
||||||
|
|
||||||
|
[menu]
|
||||||
|
[[menu.after]]
|
||||||
|
name = "HTTP API Docs"
|
||||||
|
url = "https://rustybever.be/docs/vieter/api/"
|
||||||
|
weight = 10
|
||||||
|
[[menu.after]]
|
||||||
|
name = "Man Pages"
|
||||||
|
url = "https://rustybever.be/man/vieter/vieter.1.html"
|
||||||
|
weight = 20
|
||||||
|
[[menu.after]]
|
||||||
|
name = "Vieter"
|
||||||
|
url = "https://git.rustybever.be/vieter-v/vieter"
|
||||||
|
weight = 30
|
||||||
|
[[menu.after]]
|
||||||
|
name = "Hugo Theme"
|
||||||
|
url = "https://github.com/alex-shpak/hugo-book"
|
||||||
|
weight = 40
|
||||||
|
|
||||||
|
[params]
|
||||||
|
# (Optional, default light) Sets color theme: light, dark or auto.
|
||||||
|
# Theme 'auto' switches between dark and light modes based on browser/os preferences
|
||||||
|
BookTheme = 'auto'
|
||||||
|
|
||||||
|
# (Optional, default true) Controls table of contents visibility on right side of pages.
|
||||||
|
# Start and end levels can be controlled with markup.tableOfContents setting.
|
||||||
|
# You can also specify this parameter per page in front matter.
|
||||||
|
BookToC = true
|
||||||
|
|
||||||
|
# (Optional, default none) Set the path to a logo for the book. If the logo is
|
||||||
|
# /static/logo.png then the path would be logo.png
|
||||||
|
# BookLogo = 'logo.png'
|
||||||
|
|
||||||
|
# (Optional, default none) Set leaf bundle to render as side menu
|
||||||
|
# When not specified file structure and weights will be used
|
||||||
|
# BookMenuBundle = '/menu'
|
||||||
|
|
||||||
|
# (Optional, default docs) Specify root page to render child pages as menu.
|
||||||
|
# Page is resoled by .GetPage function: https://gohugo.io/functions/getpage/
|
||||||
|
# For backward compatibility you can set '*' to render all sections to menu. Acts same as '/'
|
||||||
|
BookSection = '/'
|
||||||
|
|
||||||
|
# Set source repository location.
|
||||||
|
# Used for 'Last Modified' and 'Edit this page' links.
|
||||||
|
BookRepo = 'https://git.rustybever.be/vieter-v/vieter'
|
||||||
|
|
||||||
|
# (Optional, default 'commit') Specifies commit portion of the link to the page's last modified
|
||||||
|
# commit hash for 'doc' page type.
|
||||||
|
# Requires 'BookRepo' param.
|
||||||
|
# Value used to construct a URL consisting of BookRepo/BookCommitPath/<commit-hash>
|
||||||
|
# Github uses 'commit', Bitbucket uses 'commits'
|
||||||
|
BookCommitPath = 'src/commit'
|
||||||
|
|
||||||
|
# Enable "Edit this page" links for 'doc' page type.
|
||||||
|
# Disabled by default. Uncomment to enable. Requires 'BookRepo' param.
|
||||||
|
# Edit path must point to root directory of repo.
|
||||||
|
# BookEditPath = 'edit/main/exampleSite'
|
||||||
|
|
||||||
|
# Configure the date format used on the pages
|
||||||
|
# - In git information
|
||||||
|
# - In blog posts
|
||||||
|
BookDateFormat = 'January 2, 2006'
|
||||||
|
|
||||||
|
# (Optional, default true) Enables search function with flexsearch,
|
||||||
|
# Index is built on fly, therefore it might slowdown your website.
|
||||||
|
# Configuration for indexing can be adjusted in i18n folder per language.
|
||||||
|
BookSearch = true
|
||||||
|
|
||||||
|
# (Optional, default true) Enables comments template on pages
|
||||||
|
# By default partals/docs/comments.html includes Disqus template
|
||||||
|
# See https://gohugo.io/content-management/comments/#configure-disqus
|
||||||
|
# Can be overwritten by same param in page frontmatter
|
||||||
|
BookComments = false
|
||||||
|
|
||||||
|
# /!\ This is an experimental feature, might be removed or changed at any time
|
||||||
|
# (Optional, experimental, default false) Enables portable links and link checks in markdown pages.
|
||||||
|
# Portable links meant to work with text editors and let you write markdown without {{< relref >}} shortcode
|
||||||
|
# Theme will print warning if page referenced in markdown does not exists.
|
||||||
|
BookPortableLinks = true
|
||||||
|
|
||||||
|
# /!\ This is an experimental feature, might be removed or changed at any time
|
||||||
|
# (Optional, experimental, default false) Enables service worker that caches visited pages and resources for offline use.
|
||||||
|
BookServiceWorker = true
|
||||||
|
|
||||||
|
# /!\ This is an experimental feature, might be removed or changed at any time
|
||||||
|
# (Optional, experimental, default false) Enables a drop-down menu for translations only if a translation is present.
|
||||||
|
BookTranslatedOnly = false
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Vieter
|
||||||
|
|
||||||
|
{{< hint warning >}}
|
||||||
|
**Important**
|
||||||
|
Because this project is still in heavy development, this documentation tries to
|
||||||
|
follow the development branch & not the latest release. This means that the
|
||||||
|
documentation might not be relevant anymore for the latest release.
|
||||||
|
{{< /hint >}}
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Vieter consists of two main parts, namely an implementation of an Arch
|
||||||
|
repository server & a scheduling system to periodically build Pacman packages &
|
||||||
|
publish them to a repository.
|
||||||
|
|
||||||
|
{{< hint info >}}
|
||||||
|
**Note**
|
||||||
|
While I mention Vieter being an "Arch" repository server, it works with any
|
||||||
|
distribution that uses Pacman as the package manager. I do recommend using a
|
||||||
|
base docker image for your distribution if you wish to use the build system as
|
||||||
|
well.
|
||||||
|
{{< /hint >}}
|
||||||
|
|
||||||
|
### Why?
|
||||||
|
|
||||||
|
Vieter is my personal solution to a problem I've been facing for months:
|
||||||
|
extremely long AUR package build times. I run EndeavourOS on both my laptops,
|
||||||
|
one of which being a rather old MacBook Air. I really like being a beta-tester
|
||||||
|
for projects & run development builds for multiple packages (nheko,
|
||||||
|
newsflash...). Because of this, I have to regularly re-build these packages in
|
||||||
|
order to stay up to date with development. However, these builds can take a
|
||||||
|
really long time on the old MacBook. This project is a solution to that
|
||||||
|
problem: instead of building the packages locally, I can build them
|
||||||
|
automatically in the cloud & just download them whenever I update my system!
|
||||||
|
Thanks to this solution, I'm able to shave 10-15 minutes off my update times,
|
||||||
|
just from not having to compile everything every time there's an update.
|
||||||
|
|
||||||
|
Besides this, it's also just really useful to have a repository server that you
|
||||||
|
control & can upload your own packages to. For example, I package my st
|
||||||
|
terminal using a CI pipeline & upload it to my repository!
|
||||||
|
|
||||||
|
### Why V?
|
||||||
|
|
||||||
|
I had been interested in learning V for a couple of months ever since I
|
||||||
|
stumbled upon it by accident. It looked like a promising language & turned out
|
||||||
|
to be very fun to use! It's fast & easy to learn, & it's a nice contrast with
|
||||||
|
my usual Rust-based projects, which tend to get quite complex.
|
||||||
|
|
||||||
|
I recommend checking out their [homepage](https://vlang.io/)!
|
||||||
|
|
||||||
|
### What's with the name?
|
||||||
|
|
||||||
|
Before deciding to write this project in V, I wrote a prototype in Python,
|
||||||
|
called [Pieter](https://git.rustybever.be/Chewing_Bever/pieter). The name
|
||||||
|
Pieter came from Pieter Post, the Dutch name for [Postname
|
||||||
|
Pat](https://en.wikipedia.org/wiki/Postman_Pat). The idea was that the server
|
||||||
|
"delivered packages", & a good friend of mine suggested the name. When I
|
||||||
|
decided to switch over to Vieter, I changed the P (for Python) to a V, it
|
||||||
|
seemed fitting.
|
|
@ -0,0 +1,138 @@
|
||||||
|
---
|
||||||
|
weight: 20
|
||||||
|
---
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
By default, all vieter commands try to read in the TOML file `~/.vieterrc` for
|
||||||
|
configuration. The location of this file can be changed by using the `-f` flag.
|
||||||
|
|
||||||
|
If the above file doesn't exist or you wish to override some of its settings,
|
||||||
|
configuration is also possible using environment variables. Every variable in
|
||||||
|
the config file has a respective environment variable of the following form:
|
||||||
|
say the variable is called `api_key`, then the respective environment variable
|
||||||
|
would be `VIETER_API_KEY`. In essence, it's the variable in uppercase prepended
|
||||||
|
with `VIETER_`.
|
||||||
|
|
||||||
|
If a variable is both present in the config file & as an environment variable,
|
||||||
|
the value in the environment variable is used.
|
||||||
|
|
||||||
|
{{< hint info >}}
|
||||||
|
**Note**
|
||||||
|
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
|
||||||
|
secrets file.
|
||||||
|
{{< /hint >}}
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
The first argument passed to Vieter determines which command you wish to use.
|
||||||
|
Each of these can contain subcommands (e.g. `vieter targets list`), but all
|
||||||
|
subcommands will use the same configuration. Below you can find the
|
||||||
|
configuration variable required for each command.
|
||||||
|
|
||||||
|
### `vieter server`
|
||||||
|
|
||||||
|
* `port`: HTTP port to run on
|
||||||
|
* Default: `8000`
|
||||||
|
* `log_level`: log verbosity level. Value should be one of `FATAL`, `ERROR`,
|
||||||
|
`WARN`, `INFO` or `DEBUG`.
|
||||||
|
* Default: `WARN`
|
||||||
|
* `pkg_dir`: where Vieter should store the actual package archives.
|
||||||
|
* `data_dir`: where Vieter stores the repositories, log file & database.
|
||||||
|
* `api_key`: the API key to use when authenticating requests.
|
||||||
|
* `default_arch`: this setting serves two main purposes:
|
||||||
|
* Packages with architecture `any` are always added to this architecture.
|
||||||
|
This prevents the server from being confused when an `any` package is
|
||||||
|
published as the very first package for a repository.
|
||||||
|
* 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`
|
||||||
|
|
||||||
|
* `log_level`: log verbosity level. Value should be one of `FATAL`, `ERROR`,
|
||||||
|
`WARN`, `INFO` or `DEBUG`.
|
||||||
|
* Default: `WARN`
|
||||||
|
* `log_file`: log file to write logs to.
|
||||||
|
* Default: `vieter.log` (in `data_dir`)
|
||||||
|
* `address`: *public* URL of the Vieter repository server to build for. From
|
||||||
|
this server the list of Git repositories is 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.
|
||||||
|
* `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_concurrent_builds`: how many builds to run at the same time.
|
||||||
|
* Default: `1`
|
||||||
|
* `api_update_frequency`: how frequently (in minutes) to poll the Vieter
|
||||||
|
repository server for a new list of Git repositories to build.
|
||||||
|
* Default: `15`
|
||||||
|
* `image_rebuild_frequency`: Vieter periodically builds a builder image using
|
||||||
|
the configured base image. This makes sure build containers do not have to
|
||||||
|
download a lot of packages when updating their system. This setting defines
|
||||||
|
how frequently (in minutes) to rebuild this builder image.
|
||||||
|
* Default: `1440` (every 24 hours)
|
||||||
|
* `global_schedule`: build schedule for any Git repository that does not have a
|
||||||
|
schedule defined. For information about this syntax, see
|
||||||
|
[here](/usage/builds/schedule).
|
||||||
|
* Default: `0 3` (3AM every night)
|
||||||
|
|
||||||
|
### `vieter logs`
|
||||||
|
|
||||||
|
* `api_key`: the API key to use when authenticating requests.
|
||||||
|
* `address`: Base URL of your Vieter instance, e.g. https://example.com
|
||||||
|
|
||||||
|
### `vieter targets`
|
||||||
|
|
||||||
|
* `api_key`: the API key to use when authenticating requests.
|
||||||
|
* `address`: Base URL of your Vieter instance, e.g. https://example.com
|
||||||
|
* `base_image`: image to use when building a package using `vieter targets
|
||||||
|
build`.
|
||||||
|
* 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`)
|
|
@ -0,0 +1,114 @@
|
||||||
|
---
|
||||||
|
weight: 10
|
||||||
|
---
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
Vieter consists of a single binary, akin to busybox. The binary's behavior is
|
||||||
|
determined by its CLI arguments, e.g. `vieter server` starts the repository
|
||||||
|
server.
|
||||||
|
|
||||||
|
All installation solutions can be configured the same way,
|
||||||
|
as described [here](/configuration).
|
||||||
|
|
||||||
|
## Docker
|
||||||
|
|
||||||
|
Docker images are published to the
|
||||||
|
[`chewingbever/vieter`](https://hub.docker.com/r/chewingbever/vieter) Docker
|
||||||
|
Hub repository. You can either pull a release tag (e.g.
|
||||||
|
`chewingbever/vieter:0.1.0-rc1`), or pull the `chewingbever/vieter:dev` tag.
|
||||||
|
The latter is updated every time a new commit is pushed to the development
|
||||||
|
branch. This branch will be the most up to date, but does not give any
|
||||||
|
guarantees about stability, so beware!
|
||||||
|
|
||||||
|
Thanks to the single-binary design of Vieter, this image can be used both for
|
||||||
|
the repository server, the cron daemon and the agent.
|
||||||
|
|
||||||
|
Below is a minimal compose file to set up both the repository server & a build
|
||||||
|
agent:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
server:
|
||||||
|
image: 'chewingbever/vieter:0.5.0-rc.1'
|
||||||
|
restart: 'always'
|
||||||
|
|
||||||
|
environment:
|
||||||
|
- 'VIETER_API_KEY=secret'
|
||||||
|
- 'VIETER_DEFAULT_ARCH=x86_64'
|
||||||
|
volumes:
|
||||||
|
- 'data:/data'
|
||||||
|
|
||||||
|
cron:
|
||||||
|
image: 'chewingbever/vieter:0.5.0-rc.1'
|
||||||
|
restart: 'always'
|
||||||
|
# Required to connect to the Docker daemon
|
||||||
|
user: root
|
||||||
|
command: 'vieter agent'
|
||||||
|
|
||||||
|
environment:
|
||||||
|
- 'VIETER_API_KEY=secret'
|
||||||
|
# MUST be public URL of Vieter repository
|
||||||
|
- 'VIETER_ADDRESS=https://example.com'
|
||||||
|
# Architecture for which the agent builds
|
||||||
|
- 'VIETER_ARCH=x86_64'
|
||||||
|
- 'VIETER_MAX_CONCURRENT_BUILDS=2'
|
||||||
|
volumes:
|
||||||
|
- '/var/run/docker.sock:/var/run/docker.sock'
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
data:
|
||||||
|
```
|
||||||
|
|
||||||
|
If you do not require the build system, the repository server can be used
|
||||||
|
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 >}}
|
||||||
|
**Note**
|
||||||
|
Builds are executed on the agent's system using the host's Docker daemon. An
|
||||||
|
agent for a specific `arch` will only build packages for that specific
|
||||||
|
architecture. Therefore, if you wish to build packages for both `x86_64` &
|
||||||
|
`aarch64`, you'll have to deploy two agents, one on each architecture.
|
||||||
|
Afterwards, any Git repositories enabled for those two architectures will build
|
||||||
|
on both.
|
||||||
|
{{< /hint >}}
|
||||||
|
|
||||||
|
## Binary
|
||||||
|
|
||||||
|
On the
|
||||||
|
[releases](https://git.rustybever.be/vieter-v/vieter/releases)
|
||||||
|
page, you can find statically compiled binaries for all
|
||||||
|
released versions. This is the same binary as used inside
|
||||||
|
the Docker images.
|
||||||
|
|
||||||
|
## Arch
|
||||||
|
|
||||||
|
I publish both development & release versions of Vieter to my personal
|
||||||
|
repository, https://arch.r8r.be. Packages are available for `x86_64` &
|
||||||
|
`aarch64`. To use the repository, add the following to your `pacman.conf`:
|
||||||
|
|
||||||
|
```
|
||||||
|
[vieter]
|
||||||
|
Server = https://arch.r8r.be/$repo/$arch
|
||||||
|
SigLevel = Optional
|
||||||
|
```
|
||||||
|
|
||||||
|
Afterwards, you can update your system & install the `vieter` package for the
|
||||||
|
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
|
||||||
|
|
||||||
|
The project [README](https://git.rustybever.be/vieter-v/vieter#building)
|
||||||
|
contains instructions for building Vieter from source.
|
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
weight: 30
|
||||||
|
---
|
|
@ -0,0 +1,51 @@
|
||||||
|
---
|
||||||
|
weight: 20
|
||||||
|
---
|
||||||
|
# Building packages
|
||||||
|
|
||||||
|
The automatic build system is what makes Vieter very useful as a replacement
|
||||||
|
for an AUR helper. It can perodically build packages & publish them to your
|
||||||
|
personal Vieter repository server, removing the need to build the packages
|
||||||
|
locally.
|
||||||
|
|
||||||
|
## Adding builds
|
||||||
|
|
||||||
|
Before the cron system can start building your package, you need to add its
|
||||||
|
info to the system. The Vieter repository server exposes an HTTP API for this
|
||||||
|
(see the [HTTP API Docs](https://rustybever.be/docs/vieter/api/) for more
|
||||||
|
info). For ease of use, the Vieter binary contains a CLI interface for
|
||||||
|
interacting with this API (see [Configuration](/configuration) for
|
||||||
|
configuration details). The [man
|
||||||
|
pages](https://rustybever.be/man/vieter/vieter-targets.1.html) describe this in
|
||||||
|
greater detail, but the basic usage is as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
vieter targets add some-url some-repository
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
expects the same format as an AUR Git repository, so you can directly use AUR
|
||||||
|
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-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
|
||||||
|
more useable. For information on how to modify all parameters using the CLI,
|
||||||
|
see
|
||||||
|
[vieter-targets(1)](https://rustybever.be/man/vieter/vieter-targets.1.html).
|
||||||
|
|
||||||
|
## Reading logs
|
||||||
|
|
||||||
|
The logs of each build are uploaded to the Vieter repository server, along with
|
||||||
|
information about the exit code of the build container, when the build
|
||||||
|
started/ended etc. These logs can then be accessed using the [HTTP
|
||||||
|
API](https://rustybever.be/docs/vieter/api/).
|
||||||
|
|
||||||
|
For ease of use, the logs are also available using some CLI commands; see
|
||||||
|
[vieter-logs(1)](https://rustybever.be/man/vieter/vieter-logs.1.html) for more
|
||||||
|
information.
|
|
@ -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 >}}
|
|
@ -0,0 +1,46 @@
|
||||||
|
---
|
||||||
|
weight: 10
|
||||||
|
---
|
||||||
|
|
||||||
|
# Cron schedule syntax
|
||||||
|
|
||||||
|
The Vieter cron daemon uses a subset of the cron expression syntax to schedule
|
||||||
|
builds.
|
||||||
|
|
||||||
|
## Format
|
||||||
|
|
||||||
|
`a b c d`
|
||||||
|
|
||||||
|
* `a`: minutes
|
||||||
|
* `b`: hours
|
||||||
|
* `c`: days
|
||||||
|
* `d`: months
|
||||||
|
|
||||||
|
An expression consists of two to four sections. If less than four sections are
|
||||||
|
provided, the parser will append `*` until there are four sections. This means
|
||||||
|
that `0 3` is the same as `0 3 * *`.
|
||||||
|
|
||||||
|
Each section consists of one or more parts, separated by a comma. Each of these
|
||||||
|
parts, in turn, can be one of the following (any letters are integers):
|
||||||
|
|
||||||
|
* `*`: allow all possible values.
|
||||||
|
* `a`: only this value is allowed.
|
||||||
|
* `*/n`: allow every n-th value.
|
||||||
|
* `a/n`: allow every n-th value, starting at a in the list.
|
||||||
|
* `a-b`: allow every value between a and b, bounds included.
|
||||||
|
* `a-b/n`: allow every n-th value inside the list of values between a and b,
|
||||||
|
bounds included.
|
||||||
|
|
||||||
|
Each section can consist of as many of these parts as necessary.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
* `0 3`: every day at 03:00AM.
|
||||||
|
* `0 0 */7`: every 7th day of the month, at midnight.
|
||||||
|
|
||||||
|
## CLI tool
|
||||||
|
|
||||||
|
The Vieter binary contains a command that shows you the next matching times for
|
||||||
|
a given expression. This can be useful for understanding the syntax. For more
|
||||||
|
information, see
|
||||||
|
[vieter-schedule(1)](https://rustybever.be/man/vieter/vieter-schedule.1.html).
|
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
weight: 10
|
||||||
|
---
|
||||||
|
# Pacman repository
|
||||||
|
|
||||||
|
The part of Vieter that users will interact with the most is the Pacman
|
||||||
|
repository aka `vieter server`.
|
||||||
|
|
||||||
|
## Design overview
|
||||||
|
|
||||||
|
A Vieter repository server has support for multiple repositories, with each
|
||||||
|
repository containing packages for multiple architectures.
|
||||||
|
|
||||||
|
If you wish to use these repositories on your system, add the following to
|
||||||
|
`/etc/pacman.conf` for each repository you wish to use:
|
||||||
|
|
||||||
|
```
|
||||||
|
[repo-name]
|
||||||
|
Server = https://example.com/$repo/$arch
|
||||||
|
SigLevel = Optional
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, `$repo` and `$arch` are not variables you have to fill in yourself.
|
||||||
|
Rather, Pacman will substitute these when reading the config file. `$repo` is
|
||||||
|
replaced by the name between the square brackets (in this case `repo-name`),
|
||||||
|
and `$arch` is replaced by your system's architecture, e.g. `x86_64`. Of
|
||||||
|
course, you can also fill in these values manually yourself, e.g. if you wish
|
||||||
|
to use a different name inside the square brackets.
|
||||||
|
|
||||||
|
Important to note is that, when two repositories contain a package with the
|
||||||
|
same name, Pacman will choose the one from the repository that's highest up in
|
||||||
|
the `pacman.conf` file. Therefore, if you know your repository has packages
|
||||||
|
with the same name as ones from the official repositories, it might be better
|
||||||
|
to place the repository below the official repositories to avoid overwriting
|
||||||
|
official packages.
|
||||||
|
|
||||||
|
## Publishing packages
|
||||||
|
|
||||||
|
Packages can be easily published using a single HTTP POST request. Check out
|
||||||
|
the [HTTP API docs](https://rustybever.be/docs/vieter/api/) for more info on
|
||||||
|
these routes, including example cURL commands.
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"Target":"book.min.97cfda4f5e3c9fa49a2bf8d401f4ddc0eec576c99cdcf6afbec19173200c37db.css","MediaType":"text/css","Data":{"Integrity":"sha256-l8/aT148n6SaK/jUAfTdwO7Fdsmc3PavvsGRcyAMN9s="}}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{"Target":"book.min.97cfda4f5e3c9fa49a2bf8d401f4ddc0eec576c99cdcf6afbec19173200c37db.css","MediaType":"text/css","Data":{"Integrity":"sha256-l8/aT148n6SaK/jUAfTdwO7Fdsmc3PavvsGRcyAMN9s="}}
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 4ef38f3bbf5dae9a11a711d2ed1ced9294c6af5f
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
||||||
|
}
|
|
@ -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()
|
||||||
|
}
|
|
@ -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_)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -3,14 +3,31 @@ module build
|
||||||
import docker
|
import docker
|
||||||
import encoding.base64
|
import encoding.base64
|
||||||
import time
|
import time
|
||||||
import git
|
|
||||||
import os
|
import os
|
||||||
|
import strings
|
||||||
|
import util
|
||||||
|
import models { BuildConfig, Target }
|
||||||
|
|
||||||
const container_build_dir = '/build'
|
const (
|
||||||
|
container_build_dir = '/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']
|
||||||
|
)
|
||||||
|
|
||||||
const build_image_repo = 'vieter-build'
|
// create_build_image creates a builder image given some base image which can
|
||||||
|
// then be used to build & package Arch images. It mostly just updates the
|
||||||
|
// system, install some necessary packages & creates a non-root user to run
|
||||||
|
// makepkg with. The base image should be some Linux distribution that uses
|
||||||
|
// Pacman as its package manager.
|
||||||
|
pub fn create_build_image(base_image string) !string {
|
||||||
|
mut dd := docker.new_conn()!
|
||||||
|
|
||||||
|
defer {
|
||||||
|
dd.close() or {}
|
||||||
|
}
|
||||||
|
|
||||||
fn create_build_image(base_image string) ?string {
|
|
||||||
commands := [
|
commands := [
|
||||||
// Update repos & install required packages
|
// Update repos & install required packages
|
||||||
'pacman -Syu --needed --noconfirm base-devel git'
|
'pacman -Syu --needed --noconfirm base-devel git'
|
||||||
|
@ -28,7 +45,7 @@ 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']
|
||||||
}
|
}
|
||||||
|
@ -40,94 +57,113 @@ 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
|
||||||
docker.pull_image(image_name, image_tag) ?
|
dd.image_pull(image_name, image_tag)!
|
||||||
|
|
||||||
id := docker.create_container(c) ?
|
id := dd.container_create(c)!.id
|
||||||
docker.start_container(id) ?
|
// id := docker.create_container(c)!
|
||||||
|
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 := docker.inspect_container(id) ?
|
data := dd.container_inspect(id)!
|
||||||
|
|
||||||
if !data.state.running {
|
if !data.state.running {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for 5 seconds
|
time.sleep(1 * time.second)
|
||||||
time.sleep(5000000000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, we create the image from the container
|
// Finally, we create the image from the container
|
||||||
// As the tag, we use the epoch value
|
// As the tag, we use the epoch value
|
||||||
|
// TODO also add the base image's name into the image name to prevent
|
||||||
|
// conflicts.
|
||||||
tag := time.sys_mono_now().str()
|
tag := time.sys_mono_now().str()
|
||||||
image := docker.create_image_from_container(id, 'vieter-build', tag) ?
|
image := dd.image_from_container(id, 'vieter-build', tag)!
|
||||||
docker.remove_container(id) ?
|
dd.container_remove(id)!
|
||||||
|
|
||||||
return image.id
|
return image.id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(conf Config) ? {
|
pub struct BuildResult {
|
||||||
build_arch := os.uname().machine
|
pub:
|
||||||
|
start_time time.Time
|
||||||
// We get the repos map from the Vieter instance
|
end_time time.Time
|
||||||
repos_map := git.get_repos(conf.address, conf.api_key) ?
|
exit_code int
|
||||||
|
logs string
|
||||||
// We filter out any repos that aren't allowed to be built on this
|
}
|
||||||
// architecture
|
|
||||||
filtered_repos := repos_map.keys().map(repos_map[it]).filter(it.arch.contains(build_arch))
|
// build_target builds the given target. Internally it calls `build_config`.
|
||||||
|
pub fn build_target(address string, api_key string, base_image_id string, target &Target, force bool, timeout int) !BuildResult {
|
||||||
// No point in doing work if there's no repos present
|
config := target.as_build_config(base_image_id, force, timeout)
|
||||||
if filtered_repos.len == 0 {
|
|
||||||
return
|
return build_config(address, api_key, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, we create a base image which has updated repos n stuff
|
// build_config builds, packages & publishes a given Arch package based on the
|
||||||
image_id := create_build_image(conf.base_image) ?
|
// provided target. The base image ID should be of an image previously created
|
||||||
|
// by create_build_image. It returns the logs of the container.
|
||||||
for repo in filtered_repos {
|
pub fn build_config(address string, api_key string, config BuildConfig) !BuildResult {
|
||||||
// TODO what to do with PKGBUILDs that build multiple packages?
|
mut dd := docker.new_conn()!
|
||||||
commands := [
|
|
||||||
'git clone --single-branch --depth 1 --branch $repo.branch $repo.url repo',
|
defer {
|
||||||
'cd repo',
|
dd.close() or {}
|
||||||
'makepkg --nobuild --nodeps',
|
}
|
||||||
'source PKGBUILD',
|
|
||||||
// The build container checks whether the package is already
|
build_arch := os.uname().machine
|
||||||
// present on the server
|
build_script := create_build_script(address, config, build_arch)
|
||||||
'curl --head --fail $conf.address/$repo.repo/$build_arch/\$pkgname-\$pkgver-\$pkgrel && exit 0',
|
|
||||||
'MAKEFLAGS="-j\$(nproc)" makepkg -s --noconfirm --needed && for pkg in \$(ls -1 *.pkg*); do curl -XPOST -T "\$pkg" -H "X-API-KEY: \$API_KEY" $conf.address/$repo.repo/publish; done',
|
// We convert the build script into a base64 string, which then gets passed
|
||||||
]
|
// to the container as an env var
|
||||||
|
base64_script := base64.encode_str(build_script)
|
||||||
// We convert the list of commands into a base64 string, which then gets
|
|
||||||
// passed to the container as an env var
|
c := docker.NewContainer{
|
||||||
cmds_str := base64.encode_str(commands.join('\n'))
|
image: '${config.base_image}'
|
||||||
|
env: [
|
||||||
c := docker.NewContainer{
|
'BUILD_SCRIPT=${base64_script}',
|
||||||
image: '$image_id'
|
'API_KEY=${api_key}',
|
||||||
env: ['BUILD_SCRIPT=$cmds_str', 'API_KEY=$conf.api_key']
|
// `archlinux:base-devel` does not correctly set the path variable,
|
||||||
entrypoint: ['/bin/sh', '-c']
|
// causing certain builds to fail. This fixes it.
|
||||||
cmd: ['echo \$BUILD_SCRIPT | base64 -d | /bin/bash -e']
|
'PATH=${build.path_dirs.join(':')}',
|
||||||
work_dir: '/build'
|
]
|
||||||
user: 'builder:builder'
|
entrypoint: ['/bin/sh', '-c']
|
||||||
}
|
cmd: ['echo \$BUILD_SCRIPT | base64 -d | /bin/bash -e']
|
||||||
|
work_dir: '/build'
|
||||||
id := docker.create_container(c) ?
|
user: '0:0'
|
||||||
docker.start_container(id) ?
|
}
|
||||||
|
|
||||||
// This loop waits until the container has stopped, so we can remove it after
|
id := dd.container_create(c)!.id
|
||||||
for {
|
dd.container_start(id)!
|
||||||
data := docker.inspect_container(id) ?
|
|
||||||
|
mut data := dd.container_inspect(id)!
|
||||||
if !data.state.running {
|
start_time := time.now()
|
||||||
break
|
|
||||||
}
|
// This loop waits until the container has stopped, so we can remove it after
|
||||||
|
for data.state.running {
|
||||||
// Wait for 5 seconds
|
if time.now() - start_time > config.timeout * time.second {
|
||||||
time.sleep(5000000000)
|
dd.container_kill(id)!
|
||||||
}
|
dd.container_remove(id)!
|
||||||
|
|
||||||
docker.remove_container(id) ?
|
return error('Build killed due to timeout (${config.timeout}s)')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally, we remove the builder image
|
time.sleep(1 * time.second)
|
||||||
docker.remove_image(image_id) ?
|
|
||||||
|
data = dd.container_inspect(id)!
|
||||||
|
}
|
||||||
|
|
||||||
|
mut logs_stream := dd.container_get_logs(id)!
|
||||||
|
|
||||||
|
// Read in the entire stream
|
||||||
|
mut logs_builder := strings.new_builder(10 * 1024)
|
||||||
|
util.reader_to_writer(mut logs_stream, mut logs_builder)!
|
||||||
|
|
||||||
|
dd.container_remove(id)!
|
||||||
|
|
||||||
|
return BuildResult{
|
||||||
|
start_time: data.state.start_time
|
||||||
|
end_time: data.state.end_time
|
||||||
|
exit_code: data.state.exit_code
|
||||||
|
logs: logs_builder.str()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
module build
|
|
||||||
|
|
||||||
import cli
|
|
||||||
import env
|
|
||||||
|
|
||||||
pub struct Config {
|
|
||||||
pub:
|
|
||||||
api_key string
|
|
||||||
address string
|
|
||||||
base_image string = 'archlinux:base-devel'
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmd returns the cli submodule that handles the build process
|
|
||||||
pub fn cmd() cli.Command {
|
|
||||||
return cli.Command{
|
|
||||||
name: 'build'
|
|
||||||
description: 'Run the build process.'
|
|
||||||
execute: fn (cmd cli.Command) ? {
|
|
||||||
config_file := cmd.flags.get_string('config-file') ?
|
|
||||||
conf := env.load<Config>(config_file) ?
|
|
||||||
|
|
||||||
build(conf) ?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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()
|
||||||
|
}
|
|
@ -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'
|
||||||
|
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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,93 @@
|
||||||
|
module build
|
||||||
|
|
||||||
|
import models { BuildConfig }
|
||||||
|
|
||||||
|
// escape_shell_string escapes any characters that could be interpreted
|
||||||
|
// incorrectly by a shell. The resulting value should be safe to use inside an
|
||||||
|
// echo statement.
|
||||||
|
fn escape_shell_string(s string) string {
|
||||||
|
return s.replace(r'\', r'\\').replace("'", r"'\''")
|
||||||
|
}
|
||||||
|
|
||||||
|
// echo_commands takes a list of shell commands & prepends each one with
|
||||||
|
// an echo call displaying said command.
|
||||||
|
pub fn echo_commands(cmds []string) []string {
|
||||||
|
mut out := []string{cap: 2 * cmds.len}
|
||||||
|
|
||||||
|
for cmd in cmds {
|
||||||
|
out << "echo -e '+ ${escape_shell_string(cmd)}'"
|
||||||
|
out << cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// create_build_script generates a shell script that builds a given Target.
|
||||||
|
fn create_build_script(address string, config BuildConfig, build_arch string) string {
|
||||||
|
repo_url := '${address}/${config.repo}'
|
||||||
|
|
||||||
|
mut commands := [
|
||||||
|
// This will later be replaced by a proper setting for changing the
|
||||||
|
// mirrorlist
|
||||||
|
"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.
|
||||||
|
// This should however not pull in a lot of packages as long as the
|
||||||
|
// builder image is rebuilt frequently.
|
||||||
|
'pacman -Syu --needed --noconfirm',
|
||||||
|
// makepkg can't run as root
|
||||||
|
'su builder',
|
||||||
|
]
|
||||||
|
|
||||||
|
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',
|
||||||
|
'source PKGBUILD',
|
||||||
|
]
|
||||||
|
|
||||||
|
if !config.force {
|
||||||
|
// The build container checks whether the package is already present on
|
||||||
|
// the server.
|
||||||
|
commands << [
|
||||||
|
'curl -s --head --fail ${repo_url}/${build_arch}/\$pkgname-\$pkgver-\$pkgrel && exit 0',
|
||||||
|
// If the above curl command succeeds, we don't need to rebuild the
|
||||||
|
// package. However, because we're in a su shell, the exit command will
|
||||||
|
// drop us back into the root shell. Therefore, we must check whether
|
||||||
|
// we're in root so we don't proceed.
|
||||||
|
'[ "\$(id -u)" == 0 ] && exit 0',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
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')
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue