Compare commits

...

165 commits
v3.0.0 ... main

Author SHA1 Message Date
64f2103fb0
Prettying up the tab display a bit 2024-10-13 19:11:53 +08:00
d9283c8294
Autobuild-web: Separate tabs for package build logs
This one actually closes #54
2024-10-13 19:05:08 +08:00
8973f9e9bb
Updated manual 2024-10-13 19:03:18 +08:00
66009df10a
Split package build logs into their own files
This almost does away with #54
2024-10-13 19:02:03 +08:00
1560e028dc
Updated changelog 2024-10-13 16:14:48 +08:00
882172774d
Fixed typo in comment 2024-10-13 16:09:25 +08:00
9b8906fe82
Some small safety 2024-10-13 16:07:13 +08:00
2d5ee82731
Paginated logs page
By default, displaying 10 logs per page
2024-10-13 16:04:22 +08:00
fb305a3010
Pass the logs directory to autobuild -L instead of a specific log file 2024-10-13 15:13:22 +08:00
a39e7a2cdf
Dynamically assign file descriptors to queue files 2024-10-13 14:52:26 +08:00
5a816df980
Allow only directories as arguments to -L 2024-10-13 14:04:37 +08:00
bb149fbb27
Newline at the end of the file 2024-10-13 13:47:45 +08:00
e47b6028b2
Keep 'upgrade first' unchecked by default 2024-10-12 14:58:05 +08:00
ca0d6fb0ed
cron: Don't clear logs that are in-progress 2024-10-12 14:57:21 +08:00
2829a70437
Updated changelog 2024-10-12 13:58:42 +08:00
f2995b0261
Report current position in queue 2024-10-12 13:53:08 +08:00
1f2d4ec8d7
Handle job queueing properly
This closes #53
2024-10-12 13:43:49 +08:00
937b2395bb
Security: verify captcha isn't blank 2024-10-12 12:38:45 +08:00
e493947f77
Allow signing key management via web ui
This closes #49
2024-10-12 12:37:17 +08:00
a03b0721d3
Outsource signing key creation to autobuild daemon 2024-10-12 11:12:15 +08:00
52de550a39
Removed old debug code comments 2024-10-12 10:46:56 +08:00
e73c5329cc
Updated help menu and manual 2024-10-12 10:46:10 +08:00
d692d5577f
Outsource signing key creation to autobuild daemon 2024-10-12 10:40:35 +08:00
dae43e9a44
Properly save the repo name 2024-10-12 10:36:56 +08:00
f2c7ad6f13
Allow autobuild-daemon to create/delete signing keys 2024-10-12 10:30:23 +08:00
2f91860de1
Removed redundant CSS line 2024-10-12 09:52:38 +08:00
db10aa96b7
Fixed navbar display on smaller screens 2024-10-12 09:52:00 +08:00
8269e0234a
Properly set ownership of repo directory 2024-10-12 09:28:12 +08:00
2e60412edb
Create repo directory if it doesn't exist 2024-10-12 09:27:10 +08:00
a2346d1062
Create repo directory if it doesn't exist 2024-10-12 09:24:55 +08:00
896e687165
Don't force first repo to be called 'default'
This closes #48
2024-10-12 09:21:18 +08:00
4ebb8f4093
Don't force first repo to be named 'default' 2024-10-12 09:18:19 +08:00
f282df6f71
Name new repository as its proper repo name, not the signing key's name
This is related to #48
2024-10-12 09:15:00 +08:00
782f1ff118
Skip installing VM if it's currently installing/upgrading 2024-10-12 09:12:48 +08:00
65088da4a9
Skip installing VM if it's already installed 2024-10-12 09:08:59 +08:00
9a9d42282d
chown the sqlite db 2024-10-12 09:04:27 +08:00
fcf3458058
Updated changelog 2024-10-11 21:44:00 +08:00
afaf0a55cb
Updated changelog 2024-10-11 21:42:13 +08:00
f86f258e4c
Set permissions on db.sqlite 2024-10-11 21:35:09 +08:00
0be3628c46
Reset GET request if no arch was selected 2024-10-11 21:26:47 +08:00
db3f8b2e3f
Updated CSS for login page 2024-10-11 21:23:54 +08:00
c099a8245b
Implemented cron jobs
Auto-delete old builds, auto-delete old logs, auto-upgrade VMs
Settable through settings.web.php
2024-10-11 20:53:35 +08:00
faaa190bac
Make sure to restore build environment's original state if the job is interrupted
In particular, if we're cancelling, we need to make sure we clear any 'upgrading' or 'installing' files we created
2024-10-11 20:34:13 +08:00
af2e139357
Implemented credentials change on settings.web.php 2024-10-11 20:16:10 +08:00
5e50287e7c
Properly handle units 2024-10-11 20:02:17 +08:00
2bae83d4c7
Replaced improper function with bring_db_up_to_date()
This might be difficult to manage in the future
Will probably have to think about how to make this better
2024-10-11 19:36:01 +08:00
027fb0fa34
Beginning to add functionality to settings.web.php
Added tables to SQLite db for cron jobs
Added startup check to verify db structure is up to date
2024-10-11 19:26:13 +08:00
ee5336fb81
Added copyright notie to captcha.php 2024-10-11 18:19:59 +08:00
9c0a1b0ea9
Added case-insensitive captcha to the login page
The debian package php-gregwar-captcha is now a dependency of autobuild-web
2024-10-11 17:43:33 +08:00
bbffebc49d
Fixed a bug where the initrd preseed wasn't applied 2024-10-09 20:09:47 +08:00
f989a3860f
Build Farm settings page is now fully-functional in autobuild-web
Along the way, the following changes were made (which probably should've been committed separately):
1. The autobuild server now accepts an -i argument (--install-vm), which, when paired with one of the --amd64, --i386, --arm64, does exactly what you would think
2. When installing or upgrading a VM, a file called 'installing' or 'upgrading' is written to the VM's directory to warn other jobs (& autobuild-web)
3. The install_vm function in the build farm scripts is switched to cURL instead of wget. This removes wget as a dependency of autobuild
This change closes #46
2024-10-09 18:26:58 +08:00
c2e30651db
Improved formatting in manual page 2024-10-09 16:40:01 +08:00
e3edd64275
Corrected license information in source files 2024-10-08 19:49:10 +08:00
9d918507a2
Added copyright notice to source files 2024-10-08 19:46:35 +08:00
7390bfac57
Don't complain about an insecure connection if we're going over localhost 2024-10-08 19:12:31 +08:00
902a47eee0
Dividing settings pages into autobuild-web, autobuild config, and build farm pages 2024-10-08 19:05:01 +08:00
ed25168727
Moved autobuild settings 2024-10-08 19:02:07 +08:00
ad44156846
Cleaned up CSS 2024-10-08 17:01:57 +08:00
41a17fe5c4
Cleaned up CSS 2024-10-08 17:01:01 +08:00
cf542aef7c
Delete old build when deleting old log
Related to #47
2024-10-08 09:48:16 +08:00
8de6b1b3b0
Have daemon report success or failure
This change closes #45
2024-10-08 08:18:56 +08:00
463f8deaaa
Only pull logfile info if we're not returning early from a status file 2024-10-08 01:16:56 +08:00
ae59978fd0
Save build status codes on completion
This closes #45
2024-10-08 01:11:03 +08:00
685d26b423
Switched status code to single integer
Now we just flip the bits using bitwise operations
This was mentioned in #45
2024-10-08 00:43:53 +08:00
9d93f84b41
Fix login display on mobile 2024-10-07 20:12:10 +08:00
99df4bc1eb
Disable 'debian repo' distribution checkbox if repos aren't configured 2024-10-07 19:23:55 +08:00
3cc4c30c45
Update changelog 2024-10-07 19:11:31 +08:00
6b5c824de4
Patched a bug: first repo was not called 'default' 2024-10-07 19:10:55 +08:00
f7e26d018e
Updated changelog 2024-10-07 18:35:06 +08:00
6209fbe94e
Added repository menu screenshots to readme 2024-10-07 18:33:28 +08:00
8a9377cc11
Properly show label for repository menu
Better formatting
2024-10-07 18:33:12 +08:00
1c9bb0a3d8
Small sanity improvement 2024-10-07 18:16:06 +08:00
3fc3a558bb
Fixed display on mobile 2024-10-07 18:12:13 +08:00
c90fc5410f
Fixed redirect error 2024-10-07 18:11:51 +08:00
f657571d17
Removed unnecessary javascript line
Would really prefer to keep autobuild-web down to absolutely ZERO javascript
2024-10-07 17:34:01 +08:00
f26eab86cb
Allow repositories to be deleted from the management page in autobuild-web 2024-10-07 17:28:09 +08:00
21612ecb2b
Cleaned up build form action 2024-10-07 16:47:18 +08:00
f16dec7ec7
Allow dynamic redirects in redirect_and_die 2024-10-07 16:46:58 +08:00
06ae936c68
Allow users to create repositories from autobuild-web 2024-10-07 16:35:48 +08:00
2560df4426
Added gpg to dependencies list 2024-10-07 16:35:07 +08:00
11bd80d8bd
github_is_configured: check for email 2024-10-07 14:16:07 +08:00
a3f7ad9821
Abstracted get_debian_repos function 2024-10-07 13:08:44 +08:00
8383b9e9d0
Updated changelog 2024-10-07 12:18:30 +08:00
52669bb31d
Fixed typo 2024-10-07 12:10:30 +08:00
c663a9db77
Run PHP as autobuild user rather than www-data
Security improvement for autobuild-web
2024-10-07 12:08:51 +08:00
89a25894fd
Updated changelog 2024-10-06 15:07:10 +08:00
7dd9d23dc4
Fixed sed pattern in postrm 2024-10-06 15:04:57 +08:00
c0afaba57e
Adding nginx support 2024-10-06 14:59:44 +08:00
378044afaa
Adding nginx support 2024-10-06 14:57:41 +08:00
4fb87fd331
Trying to add nginx support 2024-10-06 14:51:11 +08:00
7865c06d41
fixed typo 2024-10-06 14:47:39 +08:00
5115ef1766
Adding nginx config for autobuild-web package 2024-10-06 14:45:51 +08:00
98407f5777
Verify whether build farm VMs are installed in autobuild-web 2024-10-06 13:26:46 +08:00
3890ac4564
Updated readme 2024-10-06 12:52:38 +08:00
0e30e5f8b1
Updated readme 2024-10-06 12:52:00 +08:00
cb282efdca
Updated readme 2024-10-06 12:51:14 +08:00
0fddca024d
Updated readme 2024-10-06 12:49:25 +08:00
15f204b772
Updated readme 2024-10-06 12:47:17 +08:00
5752c0e0ef
Updated readme 2024-10-06 12:45:15 +08:00
dfd1df703d
Updated readme 2024-10-06 12:44:40 +08:00
bb41ee5a39
Added autobuild-web screenshots to readme 2024-10-06 12:42:38 +08:00
04e84fea09
Better handling date strings 2024-10-06 11:34:48 +08:00
0dcd1cdb99
Patched an infinite redirect bug 2024-10-06 11:24:00 +08:00
3b2987f007
Updated changelog 2024-10-06 11:07:01 +08:00
9c653c6fb8
Only remove /var/autobuild/web if apt remove --purge 2024-10-06 11:03:55 +08:00
1ca83588ac
Removed 1,000 lines of PHP
Talk about negative code!
autobuild-web now uses the same trick as autobuild to parse the config file
Since there isn't any command-line toml parser packaged with Debian stable, we depend on the 'reserialize' package to convert the config file to JSON, and then we parse the JSON
autobuild-web now uses the same toml2json command provided by 'reserialize' to parse the config file, instead of bundling a 1,000-line toml parser
2024-10-06 10:57:40 +08:00
b2e3dfbc3e
Updated changelog 2024-10-06 00:26:58 +08:00
81072d5306
Created first version of autobuild-web package
This package provides a web frontend for autobuild accessible at localhost/autobuild
A few todo items:
(1) At the moment it only works with apache. It should work with any package providing 'httpd'
(2) The 'repositories' page hasn't been set up yet
2024-10-06 00:22:03 +08:00
84fc1790ae
Removing old unused variable 2024-10-05 22:40:27 +08:00
9c5bdd8ee6
Removed obsolete setting from config.toml 2024-10-05 20:55:12 +08:00
9d49eea874
Cleaning up the graceful interrupt function a bit 2024-10-05 17:02:32 +08:00
f88927b9fa
Only output 'queued' message once 2024-10-05 16:56:55 +08:00
404421fe45
Output 'cancelling' message if interrupted 2024-10-05 16:54:41 +08:00
99c506e257
Output 'cancelling' message if interrupted 2024-10-05 16:53:15 +08:00
f084c62608
Removed unused variable 2024-10-05 15:24:27 +08:00
fb3eef64dc
Much more sensible way to kill the daemon 2024-10-05 15:23:20 +08:00
0d95266d9a
Output 'queued' message when we're waiting to launch a VM 2024-10-05 15:00:12 +08:00
70b066abff
Allow group access to config file 2024-10-03 21:09:51 +08:00
ed7ca74df0
Allow -L option to be a directory 2024-10-03 20:45:37 +08:00
5d253b5f9e
Fixed a typo from the last change 2024-10-02 17:35:11 +08:00
f22d62bd27
Added support for multiple debian repositories
Option -d now requires an argument specifying which repo to publish to
This closes #42
2024-10-02 17:30:05 +08:00
b2e2d91d8b
Updated readme 2024-10-01 21:36:29 +08:00
cb2a668086
Updated help message and manual 2024-10-01 21:35:33 +08:00
b040533782
Fixing manual display 2024-10-01 21:30:11 +08:00
6de5fa4729
Added -u and -n options for upgrading build farm vms
-u/--upgrade simply upgrades the build farm vms and then exits
-n/--no-upgrade tells autobuild to skip upgrading build farm vms before building your packages
2024-10-01 21:17:12 +08:00
b44b4ebff7
Added locale generation to initrd preseed
This removes the possibility for dubious locale warnings in the build farm VMs which would otherwise pollute our build logs
2024-10-01 11:57:20 +08:00
5ffbd6cf80
Adding autobuild user to www-data group
Done in anticipation of the development of the web frontend
2024-10-01 11:03:51 +08:00
fe31a27d8e
Generate job id for builds before starting
Previously, the builds were saved in /var/autobuild/builds/PID
Generating a job ID based on the current timestamp + the PID reduces the chance for collisions in the event that the builds directory hasn't been cleaned
2024-10-01 10:40:35 +08:00
270b41f994
Added -L option to redirect output to a specified log file 2024-09-30 23:00:37 +08:00
6c15586d1e
Setup for Debian repo should exit properly
The 'cancel' button was previously overlooked
This closes #36
2024-09-30 21:41:40 +08:00
9ca91bac24
Updated changelog 2024-09-29 08:27:56 +08:00
cb999e2ddd
Copy config file to /var/autobuild on new installs 2024-09-29 08:21:38 +08:00
045d0ff9ab
Updated changelog 2024-09-22 14:49:24 +08:00
2c46d36398
Added config file editor to autobuild -s 2024-09-22 14:44:43 +08:00
cb4dc0a288
Added Git-based debian repo url field to config file 2024-09-22 14:20:53 +08:00
17ba2624ce
Updated changelog 2024-09-08 16:48:15 +08:00
c7dcb8dc5d
Enable contents listing by default to work with apt-file 2024-09-08 16:47:49 +08:00
ada3de254c
Updated changelog 2024-09-08 12:25:02 +08:00
3b90aa9a7b
Allow user to pull from private repos
If the user has provided the necessary credentials in their config file, there's no reason that autobuild shouldn't permit them to pull packages from private repos
2024-09-08 12:24:17 +08:00
cff30b857f
Updated changelog 2024-09-03 18:22:16 +08:00
d72c103b58
Fixed possible trouble with boot order when installing arm64 vm 2024-09-03 18:20:48 +08:00
22d3f79f99
Improved changelog parsing 2024-09-03 17:55:59 +08:00
6707fa8c59
Fixed stateless builds on arm64
Through a mindless and embarrassing oversight, the arm64 vm was not run in stateless mode. It is now
2024-09-03 17:44:05 +08:00
319c2f448f
Correctly parse changelogs containing asterisks
Closes #40
2024-09-03 17:24:40 +08:00
cd8625e28c
Updated changelog 2024-08-23 11:35:03 +08:00
970b2f78b5
Forcefully set 755 on /var/autobuild. This should fix autobuild on Ubuntu 2024-08-23 11:34:20 +08:00
988fb562b7
Updated changelog 2024-07-18 16:08:29 +08:00
0d129534a9
Output errors to the socket until we're more production-ready 2024-07-18 16:07:32 +08:00
de3a2355dc
Cover for pushing to Debian repo if source package was a .tar.gz archive 2024-07-18 16:04:27 +08:00
ead6435dbc
Help the daemon find fresh repo directories 2024-07-18 15:52:18 +08:00
c7cffc19bc
Removed non-acting line of code 2024-06-02 08:58:09 +08:00
87555d7eb1
Minor fix 2024-06-01 16:47:32 +08:00
08116c8697
Updated changelog 3.1.0 2024-06-01 16:37:25 +08:00
71c117a3b9
Updated readme 2024-06-01 16:35:51 +08:00
4b88aa0b31
Removed reference to old command option 2024-06-01 16:33:21 +08:00
736313e53e
Autobuild now creates a new Debian repo for the user
Rather than relying on the user already owning one, we just go ahead and create one (incl. signing key, etc)
This repo can be shipped either as a GitHub Pages site, or via some web server on the host machine, etc. We just create the files
This closes #35 and #27
2024-06-01 16:32:02 +08:00
c05064d093
Updated changelog 3.0.2 2024-05-31 17:14:58 +08:00
9706d1e671
Patched some bugs with v3 debian repo pushing
Github email now must be stored in config file, unfortunately
This, the username, and the access token are necessary to push changes -- previously, this wasn't a problem, when autobuild was run as the user (rather than as the _autobuild system user),
Because we could assume that the user had their git configured properly. But now that we're operating out of /var/autobuild with a system user distinct from the normal user, we need to handle this ourselves
In the meantime, reprepro was complaining about being unable to find .tar.xz files which were necessary to publish changes. I'm not sure if reprepro changed, in that it didn't require these before, or if debuild changed, in that it used to produce .tar.gz files instead of .tar.xz files, or neither changed but something else happened.
In either case, autobuild now saves tar archives of ANY kind, where before it used to only save .tar.gz files, from debuild
2024-05-31 17:13:43 +08:00
b5dc480218
Updated changelog 3.0.1 2024-05-31 16:15:19 +08:00
d8a0ac2d98
Removed (now unneeded) dependency 2024-05-31 16:14:17 +08:00
604cfa525a
Corrected outdated information in readme 2024-05-31 16:11:57 +08:00
cdd75ff788
Cleaned up manual a bit 2024-05-31 06:06:00 +08:00
40fa11f448
Updated readme 2024-05-30 19:23:55 +08:00
35ba3a2689
Updated readme 2024-05-30 19:22:08 +08:00
0b72cc7403
Updated readme 2024-05-30 19:21:23 +08:00
52 changed files with 6683 additions and 192 deletions

View file

@ -6,15 +6,17 @@ Automatically build & distribute Debian packages
## What does it do?
This program will **automatically**:
This program can **automatically**:
- Download Debian package sources from a git repo
- Retrieve Debian package sources from a git repo or .tar.gz archive
- Launch a virtual QEMU build-farm & build those packages for various architectures
- Create GitHub/Forgejo release pages for those packages
- Push those packages to a Debian Repository hosted on GitHub Pages (or similar service)
- Create a Debian Repository to distribute packages (which can be hosted either via GitHub Pages or local web server)
- Push those packages to said Debian Repository
Autobuild is also used to build & distribute itself.
@ -24,30 +26,30 @@ Autobuild is also used to build & distribute itself.
Autobuild can be easily installed via the [deb.rail5.org](https://deb.rail5.org) repository:
```
sudo curl -s -o /etc/apt/trusted.gpg.d/rail5.gpg "https://deb.rail5.org/rail5.gpg"
sudo curl -s -o /etc/apt/sources.list.d/rail5.list "https://deb.rail5.org/debian/rail5.list"
```sh
sudo curl -s -o /etc/apt/trusted.gpg.d/rail5-signing-key.gpg "https://deb.rail5.org/rail5-signing-key.gpg"
sudo curl -s -o /etc/apt/sources.list.d/rail5.list "https://deb.rail5.org/rail5.list"
sudo apt update
sudo apt install autobuild
```
### Set-up
After installing, run `autobuild -s` to complete set-up.
After installing, run `sudo autobuild -s` to complete set-up.
Your **CONFIG** file can be edited with `autobuild -c`, and is where you will tell autobuild where to find your packages, as well as other settings
Your **CONFIG** file can be found at `/var/autobuild/config.toml`
### Usage
### Command-line usage
```
autobuild --amd64 --i386 --arm64 --package my-debian-package --package my-other-debian-package --github-page
```
```
autobuild -1 -2 -3 -p my-debian-package -p my-other-debian-package -g
autobuild -123 -p https://github.com/user/my-debian-package -p /path/to/my-other-debian-package.tar.gz -g
```
The above examples will build your packages *"my-debian-package"* and *"my-other-debian-package"* (as you've set them up in the CONFIG file) and then publish them to GitHub Release pages.
The above examples will build your packages *"my-debian-package"* and *"my-other-debian-package"* and then publish them to GitHub Release pages.
```
autobuild --local -p my-debian-package -o /home/user/
@ -56,3 +58,20 @@ autobuild --local -p my-debian-package -o /home/user/
The above example will build your package *locally* (without using the virtual build farm) and save the resulting build in /home/user
See `autobuild --help`, `autobuild -h`, or `man autobuild` for a list of options and how to use them.
## Autobuild-web
A web interface is provided by the **autobuild-web** package:
```sh
sudo apt install autobuild-web
```
After installing, it will be available on your web-server at **http://your-domain-or.ip/autobuild**
| Desktop | Mobile |
| --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| <img title="" src="https://rail5.org/autobuild/autobuild-web-build-menu.png" alt="build-menu" width="800"> | <img title="" src="https://rail5.org/autobuild/autobuild-web-mobile-build-menu.jpeg" alt="build-menu" width="300"> |
| <img src="https://rail5.org/autobuild/autobuild-web-log-build-successful.png" title="" alt="log-success" width="800"> | <img src="https://rail5.org/autobuild/autobuild-web-mobile-log-build-successful.jpeg" title="" alt="log-success" width="300"> |
| <img src="https://rail5.org/autobuild/autobuild-web-repository-menu.png" title="" alt="repo-menu" width="800"> | <img src="https://rail5.org/autobuild/autobuild-web-mobile-repository-menu.jpeg" title="" alt="repo-menu" width="300"> |

View file

@ -19,10 +19,14 @@ function get_daemon_pid() {
head -n 1 "$temporary_log_file" | awk '{print $2}'
}
function get_job_id() {
sed -n '2{p;q;}' "$temporary_log_file" | awk '{print $2}'
}
function graceful_exit() {
if [[ -f "$temporary_log_file" ]]; then
pid_to_kill=$(get_daemon_pid)
touch "/tmp/autobuild.kill.$pid_to_kill"
socat -t 86400 - UNIX-CONNECT:/var/run/autobuild.socket >/dev/null 2>&1 <<<"-k $pid_to_kill" # Send kill command to daemon
tail --pid="$pid_to_kill" -f /dev/null 2>/dev/null
rm -f "/tmp/autobuild.kill.$pid_to_kill" 2>/dev/null
rm -f "$temporary_log_file" 2>/dev/null
@ -32,7 +36,7 @@ function graceful_exit() {
rm -rf "${temporary_storage_directory:?}"
fi
exit 0
exit 0
}
# getopt
@ -80,10 +84,10 @@ done
socat -t 86400 - UNIX-CONNECT:/var/run/autobuild.socket <<<"$passed_options" | tee "$temporary_log_file"
# Pull the built packages from /var/autobuild/builds and save them in the output directory
cp -r "$autobuild_storage_directory/builds/$(get_daemon_pid)/"* "$output_directory/" 2>/dev/null
cp -r "$autobuild_storage_directory/builds/$(get_job_id)/"* "$output_directory/" 2>/dev/null
# Ask the autobuild daemon to delete the builds from /var/autobuild/builds so we don't waste space
socat - UNIX-CONNECT:/var/run/autobuild.socket <<<"-r $(get_daemon_pid)" >/dev/null
socat - UNIX-CONNECT:/var/run/autobuild.socket <<<"-r $(get_job_id)" >/dev/null
# Ring the bell if the user asked for it
if [[ $ring_bell == true ]]; then

View file

@ -1,6 +1,6 @@
#!/usr/bin/env bash
# autobuild
# autobuild setup
# Copyright (C) 2024 rail5
# Free software (GNU Affero GPL v3)
@ -11,12 +11,17 @@ fi
local_storage_directory="/var/autobuild"
autobuild_directory="/usr/share/autobuild"
CONFIG_FILE="$local_storage_directory/config.toml"
window_title="Autobuild setup"
# Copy any updated files to $local_storage_directory/build-farm in case an upgrade took place
mkdir -p "$local_storage_directory/build-farm" 2>/dev/null
cp -ru --preserve=timestamps "${autobuild_directory:?}/build-farm/"* "${local_storage_directory:?}/build-farm/"
if [ ! -f "$CONFIG_FILE" ]; then
cp "$autobuild_directory/config.toml" "$CONFIG_FILE"
fi
BUILDFARM_SCRIPTS_FILE="$autobuild_directory/build-farm/scripts/scripts.sh"
# shellcheck source=./build-farm/scripts/scripts.sh
. "$BUILDFARM_SCRIPTS_FILE"
@ -25,16 +30,26 @@ function setup_single_vm() {
if [[ $# != 1 ]]; then
echo "bag args to setup_single_vm" && exit
fi
local ARCH="$1"
local ARCH="$1" vm_is_installing="" vm_is_upgrading="" ARCH_DIRECTORY=""
ARCH_DIRECTORY="$local_storage_directory/build-farm/debian-stable-$ARCH"
cd "$ARCH_DIRECTORY" || (echo ""; echo "Local storage directory is not properly configured"; exit)
vm_is_installing=$(test -f "./installing" && echo "true" || echo "false")
vm_is_upgrading=$(test -f "./upgrading" && echo "true" || echo "false")
if [[ $vm_is_installing == true || $vm_is_upgrading == true ]]; then
dialog --title "$window_title" \
--infobox "The $ARCH VM is already being installed or upgraded" 15 55; sleep 3
return
fi
rm -f "./image.qcow"
rm -f "./preseed.cfg"
cp ../preseed.cfg ./preseed.cfg
echo "1" > "installing"
download_vm_image "$ARCH" "$ARCH_DIRECTORY" 2>/dev/null | dialog --title "$window_title" \
--progressbox "Downloading $ARCH image... (Please wait)" 15 55
preseed_vm_image "$ARCH" "$ARCH_DIRECTORY" 2>/dev/null | dialog --title "$window_title" \
@ -43,6 +58,7 @@ function setup_single_vm() {
--progressbox "Installing $ARCH VM... (Please wait, this may take a while)" 15 55
rm -f "./"*.iso
rm -f "installing"
}
function setup_build_farm() {
@ -93,30 +109,598 @@ function setup_build_farm() {
}
function get_list_of_debian_repos() {
find ${local_storage_directory:?}/repo/* -maxdepth 0 -type d 2>/dev/null
}
function delete_debian_repository() {
if [[ $# != 1 ]]; then
echo "bag args to create_new_debian_repository"; exit 1
fi
local repo_path="$1"
if [[ "$(dirname "$(realpath "$repo_path")")" != "${local_storage_directory:?}/repo" ]]; then
echo "Invalid path supplied to create_new_debian_repository"; exit 1
fi
dialog --title "$window_title" \
--yesno "Are you sure you would like to DELETE '$(basename "$repo_path")'?" 15 55
case $? in
0)
rm -rf "${repo_path:?}/"
return ;;
1)
return ;;
esac
}
function create_new_debian_repository() {
if [[ $# != 1 ]]; then
echo "bag args to create_new_debian_repository"; exit 1
fi
local repo_path="$1"
if [[ "$(dirname "$(realpath "$repo_path")")" != "${local_storage_directory:?}/repo" ]]; then
echo "Invalid path supplied to create_new_debian_repository"; exit 1
fi
local user_email="" user_name="" signing_key_fingerprint="" repo_url="" repo_name="" repo_friendlyname="" default_index_html_code="" repo_will_be_ghpages=false
# Get repository name
repo_name=$(basename "$repo_path")
# Get the repo "friendly name":
## Remove non-alphanumeric characters from the repo name
## And convert the remainder to lowercase
# shellcheck disable=SC2001
repo_friendlyname=$(sed 's/[^A-Za-z0-9\-]//g' <<<"$repo_name" | tr '[:upper:]' '[:lower:]')
# Ask the user for the name and email to use for the package signing key
{ user_email="$(dialog --title "$window_title" \
--inputbox "Input the EMAIL to be associated with the package GPG signing key" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
if [[ $? -eq 1 ]]; then
# User pressed cancel
return
fi
# Check if we've already made a key with that email
if gpg --list-secret-keys "$user_email"; then
key_exists_already=true
user_name=$(gpg --with-colons -k "$user_email" | awk -F: '$1=="uid" {print $10}' | sed 's/<.\+>//')
else
key_exists_already=false
fi
if [[ $key_exists_already == false ]]; then
{ user_name="$(dialog --title "$window_title" \
--inputbox "Input the NAME to be associated with the package GPG signing key" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
if [[ $? -eq 1 ]]; then
# User pressed cancel
return
fi
# Create the new key
autobuild -C -E "$user_email" -N "$user_name"
fi
# Get the signing key's fingerprint!
signing_key_fingerprint=$(gpg -K --with-colons "$user_email" | awk -F: '$1=="fpr" {print $10}' | head -n 1)
# Delete any old repository which may or may not exist
rm -rf "${repo_path:?}/"
# Prepare the new repository for reprepro
mkdir -p "${repo_path:?}/conf"
cat > "${repo_path:?}/conf/distributions" <<EOF
Origin: $repo_friendlyname
Label: $repo_friendlyname
Codename: unstable
Architectures: source amd64 i386 arm64
Components: main
Description: Apt repository for $repo_friendlyname
SignWith: $signing_key_fingerprint
Contents: .gz
EOF
# Ask the user for the repo URL
{ repo_url="$(dialog --title "$window_title" \
--inputbox "Input the FULL URL of the Debian Repository\nFor example: https://my.site.com/path/to/repo" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
if [[ $? -eq 1 ]]; then
# User pressed cancel
return
fi
# Export the public key to a file
gpg --export "$signing_key_fingerprint" > "${repo_path:?}/${repo_friendlyname:?}-signing-key.gpg"
# Create the repo .list file
cat > "${repo_path:?}/${repo_friendlyname:?}.list" <<EOF
deb $repo_url unstable main
deb-src $repo_url unstable main
EOF
# Create the default index.html file for the repo
default_index_html_code=$(cat "${autobuild_directory:?}/repository/default-index.html")
default_index_html_code="${default_index_html_code//\%REPO_FRIENDLYNAME\%/"$repo_friendlyname"}"
default_index_html_code="${default_index_html_code//\%REPO_URL\%/"$repo_url"}"
cat > "${repo_path:?}/index.html" <<<"$default_index_html_code"
# Will this be some web server, or GitHub Pages?
# If it's GitHub Pages, we'll have to push changes etc rather than just change files
dialog --title "$window_title" \
--yesno "Will this repository be served via GitHub Pages?" 15 55
case $? in
0)
repo_will_be_ghpages=true
;;
1)
repo_will_be_ghpages=false
;;
esac
local repo_conf_file="${repo_path:?}/autobuild_repo.conf"
echo "[repo]" > "${repo_conf_file:?}"
if [[ $repo_will_be_ghpages == true ]]; then
# Parse autobuild config.toml to get GitHub Credentials
local config_json=""
config_json=$(toml2json "$CONFIG_FILE")
GITHUB_OWNER=$(jq -r ".github.repo_owner" <<<"$config_json")
GITHUB_EMAIL=$(jq -r ".github.email" <<<"$config_json")
GITHUB_ACCESS_TOKEN=$(jq -r ".github.access_token" <<<"$config_json")
if [[ "$GITHUB_OWNER" == "" || "$GITHUB_EMAIL" == "" || "$GITHUB_ACCESS_TOKEN" == "" ]]; then
echo "ghpages = false" >> "${repo_conf_file:?}"
dialog --title "$window_title" \
--infobox "Error: Your GitHub credentials are not properly configured in /var/autobuild/config.toml\n\nBefore trying again, please ensure that that file contains:\n - Your GitHub username\n - Your GitHub email\n - Your GitHub Access Token" 15 55; sleep 7
return
fi
echo "ghpages = true" >> "${repo_conf_file:?}"
# Ask the user for the Github URL
{ ghpages_url="$(dialog --title "$window_title" \
--inputbox "Input the FULL URL of the GitHub Repository that will be used\nFor example: https://github.com/user/repository.git" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
if [[ $? -eq 1 ]]; then
# User pressed cancel
return
fi
echo "ghpages_url = \"$ghpages_url\"" >> "${repo_conf_file:?}"
# Initialize the git repo, set origin, and push
cd "${repo_path:?}" || (echo ""; echo "Confusing and fatal error in setting up GitHub Pages repository"; exit)
# Add our necessary credentials to the URL to push (https://user:pass@github.com/etc)
local push_url="${ghpages_url/:\/\//:\/\/$GITHUB_OWNER:$GITHUB_ACCESS_TOKEN@}"
local temporary_repo_changes_directory=""
temporary_repo_changes_directory=$(mktemp --tmpdir -d)
mv -f "./"* "${temporary_repo_changes_directory:?}/"
git init
git config user.email "$GITHUB_EMAIL"
git config user.name "$GITHUB_OWNER"
git remote add origin "$ghpages_url"
git pull "$push_url" -q
mv -f "${temporary_repo_changes_directory:?}/"* "./"
rm -rf "${temporary_repo_changes_directory:?}"
git add --all
git commit -m "Initialized Autobuild GitHub Pages Debian Repository"
git branch -M main
git push "$push_url" --all # Push changes
else
echo "ghpages = false" >> "${repo_conf_file:?}"
fi
dialog --title "$window_title" \
--infobox "Your Debian Repo is now configured\n\nThe files are served in:\n$repo_path\n\nIf your repo is managed via GitHub Pages, it may take a few moments to publish." 15 55; sleep 7
}
function setup_debian_repo() {
if [[ $# != 1 ]]; then
echo "bad args to setup_debian_repo"; exit 1
fi
local repo_path="$1" selected_repo_is_ok=false selected_repo_exists_already="" new_repo_name=""
# First, check: Are we either (1) making a new repository ('blank' input), or (2) modifying one which *actually* exists?
# In other words, refuse to continue if we've been asked to modify something which doesn't exist,
# Or which doesn't exist *inside* the proper directory.
if [[ "$repo_path" == "" ]]; then
# Creating a new repo, automatically ok
selected_repo_is_ok=true
else
for repository in $(get_list_of_debian_repos); do
if [[ "$repository" == "$repo_path" ]]; then
selected_repo_is_ok=true
break
fi
done
fi
if [[ "$selected_repo_is_ok" == false ]]; then
# Quit.
return
fi
# If we're making a new repository, let's get a name for it
if [[ "$repo_path" == "" ]]; then
{ new_repo_name="$(dialog --title "$window_title" \
--inputbox "Enter a name for this new repository:" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
case "$new_repo_name" in
("")
# User decided to cancel
return ;;
(*[!a-zA-Z0-9\-\_]*)
# Invalid characters
# Allowed characters: a-z, A-Z, 0-9, -, and _
dialog --title "$window_title" \
--infobox "Invalid repository name!\nRepo names may contain letters, numbers, hyphens (-) and underscores (_)" 15 55;
sleep 7;
return ;;
(*)
# No problem. Move on
repo_path="$local_storage_directory/repo/$new_repo_name"
esac
fi
selected_repo_exists_already=$(test -f "${repo_path:?}/autobuild_repo.conf" && echo "true" || echo "false")
if [[ "$selected_repo_exists_already" == true ]]; then
dialog --title "$window_title" \
--yesno "Selected existing repository: $(basename "$repo_path").\nWould you like to DELETE this repository?" 15 55
case $? in
0)
delete_debian_repository "$repo_path"
return ;;
1)
return ;;
esac
else
create_new_debian_repository "$repo_path"
fi
}
function setup_debian_repos() {
local next_page="" number_of_debian_repos="" menu_command_args="" iterator=1 selected_repo_path=""
# How many (and which) Debian Repos do we have set up already?
number_of_debian_repos="$(wc -l <<<"$(get_list_of_debian_repos)")"
menu_command_args=("$((number_of_debian_repos + 1))")
for repository in $(get_list_of_debian_repos); do
menu_command_args+=("$iterator" "$(basename "$repository")")
iterator=$((iterator + 1))
done
menu_command_args+=("$iterator" "Create New Repository")
{ next_page="$(dialog --title "$window_title" --menu "Debian Repositories" 15 55 "${menu_command_args[@]}" \
2>&1 1>&3 3>&- )"; } 3>&1
if [[ "$next_page" == "" ]]; then
return # User pressed cancel
fi
selected_repo_path="$(sed "$next_page"'q;d' <<<"$(get_list_of_debian_repos)")"
setup_debian_repo "$selected_repo_path" # Otherwise, get going
}
function clear_builds_directory() {
rm -rf "${local_storage_directory:?}/builds/"*
dialog --title "$window_title" \
--infobox "Cleared $local_storage_directory/builds" 15 55; sleep 3
}
function get_field_from_config_file() {
if [[ $# != 1 ]]; then
echo "bag args to get_field_config_file" && exit 1
fi
local config_json="" field="$1" result=""
config_json="$(toml2json "$CONFIG_FILE")"
case $field in
"packages")
local package_urls=""
package_urls="$(jq -r ".packages.package_urls[]" <<<"$config_json")"
result="${package_urls//\"/}"
;;
"github-owner")
result="$(jq -r ".github.repo_owner" <<<"$config_json")"
;;
"github-email")
result="$(jq -r ".github.email" <<<"$config_json")"
;;
"github-access-token")
result="$(jq -r ".github.access_token" <<<"$config_json")"
;;
"forgejo-instance")
result="$(jq -r ".forgejo.instance_url" <<<"$config_json")"
;;
"forgejo-owner")
result="$(jq -r ".forgejo.repo_owner" <<<"$config_json")"
;;
"forgejo-access-token")
result="$(jq -r ".forgejo.access_token" <<<"$config_json")"
;;
esac
echo "$result"
}
function save_change_to_config_file() {
if [[ $# != 2 ]]; then
echo "bag args to save_change_to_config_file" && exit 1
fi
local package_urls="" github_owner="" github_email="" github_access_token="" forgejo_instance="" forgejo_owner="" forgejo_access_token="" final_config_file="" field_to_edit="$1" new_value="$2"
# First, parse the current contents of the config file
## Get the package URLs
package_urls="$(get_field_from_config_file "packages")"
## Get GitHub info
github_owner="$(get_field_from_config_file "github-owner")"
github_email="$(get_field_from_config_file "github-email")"
github_access_token="$(get_field_from_config_file "github-access-token")"
## Get Forgejo info
forgejo_instance="$(get_field_from_config_file "forgejo-instance")"
forgejo_owner="$(get_field_from_config_file "forgejo-owner")"
forgejo_access_token="$(get_field_from_config_file "forgejo-access-token")"
# Receive and process changes here
case $field_to_edit in
"packages")
package_urls="$new_value"
;;
"github-owner")
github_owner="$new_value"
;;
"github-email")
github_email="$new_value"
;;
"github-access-token")
github_access_token="$new_value"
;;
"forgejo-instance")
forgejo_instance="$new_value"
;;
"forgejo-owner")
forgejo_owner="$new_value"
;;
"forgejo-access-token")
forgejo_access_token="$new_value"
;;
esac
# Save resulting variables to the config file
final_config_file="# Autobuild configuration
[packages]
package_urls = ["
while IFS= read -r url; do
if [[ "$url" != "" ]]; then
final_config_file="$final_config_file
\"$url\","
fi
done <<<"$package_urls"
final_config_file="${final_config_file::-1}" # Remove the last appended comma from the package list
final_config_file="$final_config_file
]
[github]
# Owner username
repo_owner = \"$github_owner\"
# Github Email
email = \"$github_email\"
# Access token
access_token = \"$github_access_token\"
[forgejo]
# The distribution settings assume that the repository names are the same across github/forgejo/etc
# Location of your forgejo instance
instance_url = \"$forgejo_instance\"
# Owner username
repo_owner = \"$forgejo_owner\"
# Access token
access_token = \"$forgejo_access_token\"
"
# Now that we've constructed the file, save it
echo "$final_config_file" > "${CONFIG_FILE:?}"
}
function setup_config_packages() {
temporary_file="$(mktemp)"
## Get the list of package urls
local package_url_list=""
get_field_from_config_file "packages" > "$temporary_file"
{ package_url_list="$(dialog --title "$window_title" \
--backtitle "INFO: Enter your source package Git URLs (one URL per line)" \
--max-input 99999 \
--editbox "$temporary_file" 15 55 \
2>&1 1>&3 3>&- )"; } 3>&1
## If the input is blank, the user pressed cancel
if [[ "$package_url_list" != "" ]]; then
save_change_to_config_file "packages" "$package_url_list"
fi
rm -f "${temporary_file:?}"
}
function setup_config_field_entry() {
if [[ $# != 2 ]]; then
echo "bag args to setup_config_field_entry" && exit 1
fi
local field="$1" description="$2" value=""
value="$(get_field_from_config_file "$field")"
{ value="$(dialog --title "$window_title" \
--inputbox "Enter your $description:" 15 55 \
"$value" \
2>&1 1>&3 3>&- )"; } 3>&1
if [[ $? -eq 0 ]]; then
save_change_to_config_file "$field" "$value"
fi
}
function setup_config_github() {
local next_page=""
{ next_page="$(dialog --title "$window_title" \
--menu "GitHub settings" 15 55 \
4 \
1 "GitHub Username" \
2 "GitHub Email" \
3 "GitHub Access Token" \
4 "Back" \
2>&1 1>&3 3>&- )"; } 3>&1
case $next_page in
"" | 4) # User pressed "cancel" or "Exit"
return ;;
1)
setup_config_field_entry "github-owner" "GitHub Username"
setup_config_github
;;
2)
setup_config_field_entry "github-email" "GitHub Email"
setup_config_github
;;
3)
setup_config_field_entry "github-access-token" "GitHub Access Token"
setup_config_github
;;
esac
}
function setup_config_forgejo() {
local next_page=""
{ next_page="$(dialog --title "$window_title" \
--menu "Forgejo settings" 15 55 \
4 \
1 "Forgejo Instance URL" \
2 "Forgejo Username" \
3 "Forgejo Access Token" \
4 "Back" \
2>&1 1>&3 3>&- )"; } 3>&1
case $next_page in
"" | 4) # User pressed "cancel" or "Exit"
return ;;
1)
setup_config_field_entry "forgejo-instance" "Forgejo Instance URL"
setup_config_forgejo
;;
2)
setup_config_field_entry "forgejo-owner" "Forgejo Username"
setup_config_forgejo
;;
3)
setup_config_field_entry "forgejo-access-token" "Forgejo Access Token"
setup_config_forgejo
;;
esac
}
function setup_config_menu() {
local next_page=""
{ next_page="$(dialog --title "$window_title" \
--menu "Edit config" 15 55 \
4 \
1 "Packages" \
2 "GitHub settings" \
3 "Forgejo settings" \
4 "Exit" \
2>&1 1>&3 3>&- )"; } 3>&1
case $next_page in
"" | 4) # User pressed "cancel" or "Exit"
return ;;
1)
setup_config_packages
setup_config_menu
;;
2)
setup_config_github
setup_config_menu
;;
3)
setup_config_forgejo
setup_config_menu
;;
esac
}
function setup_main_menu() {
local next_page=""
{ next_page="$(dialog --title "$window_title" \
--menu "This menu can be reached anytime with 'autobuild -s'" 15 55 \
3 \
1 "Install build farm" \
2 "Clear 'builds' directory" \
3 "Exit" \
5 \
1 "Edit config" \
2 "Install build farm" \
3 "Configure Debian repositories" \
4 "Clear 'builds' directory" \
5 "Exit" \
2>&1 1>&3 3>&- )"; } 3>&1 # Capture stderr output into next_page variable
# 'dialog' writes user responses to stderr
case $next_page in
"" | 3) # User pressed "cancel" or "Exit"
"" | 5) # User pressed "cancel" or "Exit"
exit ;;
1)
setup_build_farm
setup_config_menu
setup_main_menu
;;
2)
setup_build_farm
setup_main_menu
;;
3)
setup_debian_repos
setup_main_menu
;;
4)
clear_builds_directory
setup_main_menu
;;

File diff suppressed because it is too large Load diff

View file

@ -404,7 +404,7 @@ tasksel tasksel/first multiselect standard, ssh-server
### Here we install build-dependencies for the packages we will build on this VM
d-i pkgsel/include string sudo build-essential gcc g++ make git wget tar curl devscripts sed unzip xz-utils jq
d-i pkgsel/include string sudo build-essential gcc g++ make git tar curl devscripts sed unzip xz-utils jq locales
### Whether to upgrade packages after debootstrap.
@ -477,11 +477,17 @@ d-i finish-install/reboot_in_progress note
### Here we configure passwordless sudo for the above-setup "debian" user
### And we also make sure to set up the locale properly
### to avoid future warnings which would pollute our build logs
d-i preseed/late_command string \
in-target mkdir -p /etc/sudoers.d/; \
echo 'debian ALL=(ALL) NOPASSWD: ALL' > /target/etc/sudoers.d/debian; \
in-target chmod 440 /etc/sudoers.d/debian;
in-target chmod 440 /etc/sudoers.d/debian; \
sed -i 's/^# *\(en_US.UTF-8\)/\1/' /target/etc/locale.gen; \
in-target locale-gen; \
echo "export LC_ALL=en_US.UTF-8" >> /target/etc/bash.bashrc; \
echo "export LANG=en_US.UTF-8" >> /target/etc/bash.bashrc; \
echo "export LANGUAGE=en_US.UTF-8" >> /target/etc/bash.bashrc;
### This is how to make the installer shutdown when finished, but not

View file

@ -27,7 +27,7 @@ function boot_vm() {
$ACCEL \
-net user,hostfwd=tcp::$SSHPORT-:22 \
-net nic \
-drive if=virtio,file=$IMAGE_DIRECTORY/image.qcow,format=qcow2,id=hd \
-drive if=virtio,file=$IMAGE,format=qcow2,id=hd \
$OPTIONAL_EXTRA_COMMAND \
-serial telnet:localhost:$TELNET_PORT,server,nowait \
-nographic"

View file

@ -9,15 +9,12 @@ function download_vm_image() {
cd "$DIRECTORY" || (echo ""; echo "download_vm_image: Could not cd into $DIRECTORY"; exit)
temporary_file=$(mktemp -p ./)
wget -O "$temporary_file" https://www.debian.org/CD/netinst/
curl -L -o "$temporary_file" https://www.debian.org/CD/netinst/
image_url=$(grep -o -P -e "https://cdimage.debian.org/debian-cd/current/$ARCH/iso-cd/debian.*?netinst.iso" "$temporary_file" | head -n1)
file_name=$(echo "$image_url" | grep -o -P -e "/debian-[0-9].*?-netinst.iso" | cut -c2-)
wget -N "$image_url"
curl -L -o "debian-$ARCH-netinst.iso" "$image_url"
rm -f "$temporary_file"
mv "$file_name" "debian-$ARCH-netinst.iso"
}
function preseed_vm_image() {
@ -106,7 +103,7 @@ function install_vm() {
ARCH_STRING="i386"
elif [[ "$ARCH" == "arm64" ]]; then
ARCH_STRING="aarch64"
OPTIONAL_EXTRA_COMMAND="-drive file=./debian-$ARCH-netinst-hl.iso,id=cdrom,if=none,media=cdrom -device virtio-scsi-device -device scsi-cd,drive=cdrom"
OPTIONAL_EXTRA_COMMAND="-drive file=./debian-$ARCH-netinst-hl.iso,id=cdrom,if=none,media=cdrom -device virtio-scsi-device -device scsi-cd,drive=cdrom,bootindex=1"
fi
IMAGE="$ARCH_DIRECTORY/image.qcow"

View file

@ -16,21 +16,22 @@ package_urls = [
[github]
# Owner username
repo_owner = "rail5"
#repo_owner = "rail5"
# Github Email
#email = "andrew@rail5.org"
# Access token
access_token = "github_access_token_here"
git_debian_repo = "https://github.com/rail5/ppa.git"
#access_token = "github_access_token_here"
[forgejo]
# The distribution settings assume that the repository names are the same across github/forgejo/etc
# Location of your forgejo instance
instance_url = "https://git.disroot.org"
#instance_url = "https://git.disroot.org"
# Owner username
repo_owner = "rail5"
#repo_owner = "rail5"
# Access token
access_token = "forgejo_access_token_here"
#access_token = "forgejo_access_token_here"

61
debian/autobuild-web.postinst vendored Executable file
View file

@ -0,0 +1,61 @@
#!/bin/bash
set -e
mkdir -p "/var/autobuild/web/log"
if [[ ! -f /var/autobuild/config.toml ]]; then
cp /usr/share/autobuild/config.toml /var/autobuild/config.toml
fi
# Set permissions on config file
chown _autobuild:nogroup /var/autobuild/config.toml
chmod 660 /var/autobuild/config.toml
# Set up for PHP sessions
mkdir -p /var/autobuild/web/sessions
chown _autobuild /var/autobuild/web/sessions
chmod 700 /var/autobuild/web/sessions
# Set RWX permissions on /var/autobuild/web
chown _autobuild:nogroup /var/autobuild/web
chown _autobuild:nogroup /var/autobuild/web/log
chmod 770 /var/autobuild/web
chmod 770 /var/autobuild/web/log
# If the database is present, make sure it has proper permissions
if [[ -f /var/autobuild/web/db.sqlite ]]; then
chown _autobuild:nogroup /var/autobuild/web/db.sqlite
chmod 644 /var/autobuild/web/db.sqlite
fi
# Configure PHP-FPM
PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
if [[ -d /etc/php/$PHP_VERSION/fpm/pool.d ]]; then
ln -sf /usr/share/autobuild-web/php-fpm/autobuild-web.conf /etc/php/$PHP_VERSION/fpm/pool.d/autobuild-web.conf
deb-systemd-invoke restart php$PHP_VERSION-fpm || true
fi
# Enable Apache autobuild conf
if [[ -e /usr/share/apache2/apache2-maintscript-helper ]]; then
. /usr/share/apache2/apache2-maintscript-helper
ln -sf /usr/share/autobuild-web/apache/autobuild-web.conf /etc/apache2/conf-available/autobuild-web.conf
apache2_invoke enmod proxy_fcgi || true
apache2_invoke enconf autobuild-web || true
deb-systemd-invoke reload apache2 || true
fi
# Enable Nginx autobuild conf
if [[ -d /etc/nginx ]]; then
ln -sf /usr/share/autobuild-web/nginx/autobuild-web.conf /etc/nginx/autobuild-web.conf
for site in /etc/nginx/sites-enabled/*; do
if [[ -f "$site" ]]; then
if ! grep -q "include /etc/nginx/autobuild-web.conf;" "$site"; then
sed -i '/^server {/a \ include /etc/nginx/autobuild-web.conf;' "$site"
fi
fi
done
deb-systemd-invoke reload nginx || true
fi
#DEBHELPER#

40
debian/autobuild-web.postrm vendored Executable file
View file

@ -0,0 +1,40 @@
#!/bin/bash
set -e
# Disable Apache autobuild conf
if [[ -e /usr/share/apache2/apache2-maintscript-helper ]]; then
. /usr/share/apache2/apache2-maintscript-helper
apache2_invoke disconf autobuild-web
deb-systemd-invoke reload apache2 || true
fi
if [[ -f /etc/nginx/autobuild-web.conf ]]; then
for site in /etc/nginx/sites-enabled/*; do
if [[ -f "$site" ]]; then
if grep -q "include /etc/nginx/autobuild-web.conf;" "$site"; then
sed -i '/include \/etc\/nginx\/autobuild-web.conf;/d' "$site"
fi
fi
done
deb-systemd-invoke reload nginx || true
fi
if [[ -x "$(command -v php)" ]]; then
PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
if [[ -f /etc/php/$PHP_VERSION/fpm/pool.d/autobuild-web.conf ]]; then
rm -f /etc/php/$PHP_VERSION/fpm/pool.d/autobuild-web.conf
deb-systemd-invoke restart php$PHP_VERSION-fpm || true
fi
fi
if [[ -d /var/autobuild/web/sessions ]]; then
rm -rf /var/autobuild/web/sessions
fi
if [[ -d /var/autobuild/web ]] && [[ "$1" == purge ]]; then
rm -rf /var/autobuild/web
fi
#DEBHELPER#

47
debian/autobuild.postinst vendored Executable file
View file

@ -0,0 +1,47 @@
#!/bin/bash
set -e
# Create the autobuild user
if ! id -u _autobuild; then
adduser --system --home /var/autobuild _autobuild
fi
# Make sure the autobuild user has permission to run VMs with KVM acceleration
if [[ $(getent group kvm) ]]; then
usermod -aG kvm _autobuild
fi
# Create the /var/autobuild directory
if [[ ! -d /var/autobuild ]]; then
mkdir -p /var/autobuild
fi
# Make sure the autobuild user owns the /var/autobuild directory
chown _autobuild /var/autobuild
# Set permissions on /var/autobuild
chmod 755 /var/autobuild
# Copy config file
if [[ ! -f /var/autobuild/config.toml ]]; then
cp /usr/share/autobuild/config.toml /var/autobuild/config.toml
fi
chown _autobuild /var/autobuild/config.toml
# Set permissions on config file
chmod 660 /var/autobuild/config.toml
# Create the /var/autobuild/repo directory
if [[ ! -d /var/autobuild/repo ]]; then
mkdir -p /var/autobuild/repo
fi
chown _autobuild /var/autobuild/repo
# Enable and start the autobuild service
deb-systemd-invoke enable autobuild.socket
deb-systemd-invoke start autobuild.socket
#DEBHELPER#

211
debian/changelog vendored
View file

@ -1,3 +1,214 @@
autobuild (3.3.2) unstable; urgency=medium
* autobuild-web: Paginated logs page
By default, displaying 10 logs per page
* autobuild-web: Pass the logs directory to autobuild -L instead of a
specific log file
* autobuild-web: Default unchecked 'upgrade first' on build.php
* autobuild-web: Patched a bug in the cron jobs that could have
potentially cleared logs which were in-progress
* autobuild: Dynamically assign file descriptors to queue files
* autobuild: Allow only directories as arguments to -L
-- rail5 <andrew@rail5.org> Sun, 13 Oct 2024 16:12:44 +0800
autobuild (3.3.1) unstable; urgency=medium
* autobuild: Handle job queueing properly
This closes #53
* autobuild-web: Security: verify captcha isn't blank
* autobuild-web: Allow signing key management via web ui
This closes #49. Outsourced signing key creation to autobuild
daemon
-- rail5 <andrew@rail5.org> Sat, 12 Oct 2024 13:57:08 +0800
autobuild (3.3.0) unstable; urgency=medium
* autobuild-web: Settings page is now split into 3 sections:
settings.web.php, settings.autobuild.php, and settings.farm.php
* autobuild-web: Implemented Build Farm management through web UI
Can install, upgrade, and remove build farm VMs through
settings.farm.php
* autobuild-web: Implemented cron jobs
Auto-delete old builds, auto-delete old logs, auto-upgrade VMs
Configurable through settings.web.php
* autobuild: Make sure to restore build environment's original state
if the job is interrupted. In particular, if we're cancelling, we
need to make sure we clear any 'upgrading' or 'installing' files we
created
* autobuild-web: Added case-insensitive captcha to the login page
The debian package php-gregwar- captcha is now a dependency of
autobuild-web
* autobuild: Fixed a bug where the initrd preseed wasn't always
properly applied
* autobuild: Now accepts an -i argument (--install-vm), which, when
paired with one of the options --amd64, --i386, --arm64, does exactly
what you would expect.
* autobuild: Swapped wget for cURL
* autobuild: Build logs now report success or failure at the end
-- rail5 <andrew@rail5.org> Fri, 11 Oct 2024 21:37:01 +0800
autobuild (3.2.5) unstable; urgency=medium
* Patched a bug: first repo was not called 'default'
-- rail5 <andrew@rail5.org> Mon, 07 Oct 2024 19:11:19 +0800
autobuild (3.2.4) unstable; urgency=medium
* Added repository menu to autobuild-web
* Added gpg to dependencies list
-- rail5 <andrew@rail5.org> Mon, 07 Oct 2024 18:34:21 +0800
autobuild (3.2.3) unstable; urgency=medium
* autobuild-web: Run PHP as _autobuild user rather than www-data
Security improvement for autobuild-web
Incidentally, this allows nginx users to run autobuild-web
without having configured PHP in their nginx conf already, since
we're now running php-fpm instead of relying on the web server's
configuration for PHP.
-- rail5 <andrew@rail5.org> Mon, 07 Oct 2024 12:16:45 +0800
autobuild (3.2.2) unstable; urgency=medium
* Added support for nginx in autobuild-web
Autobuild-web now works with either nginx or apache. Hopefully we
can add more httpd options later.
-- rail5 <andrew@rail5.org> Sun, 06 Oct 2024 15:06:23 +0800
autobuild (3.2.1) unstable; urgency=medium
* Only remove /var/autobuild/web if apt remove --purge
* Removed 1,000 lines of PHP
autobuild-web now uses 'toml2json' (provided by 'reserialize')
to parse the config file, instead of bundling a 1,000-line toml parser
* Patched bug in postinst/postrm maintainer scripts introduced in 3.2.0
which would reset your config.toml
-- rail5 <andrew@rail5.org> Sun, 06 Oct 2024 11:05:04 +0800
autobuild (3.2.0) unstable; urgency=medium
* Created first version of autobuild-web package
This package provides a web frontend for autobuild accessible at
localhost/autobuild
A few todo items:
(1) At the moment it only works with apache. It should work with any
package providing 'httpd'
(2) The 'repositories' page hasn't been set up yet
* Interrupts now kill the daemon in a much more sensible way
* Added support for managing multiple debian repositories
Option -d now requires an argument specifying which repo to publish to
* Added -u and -n options for upgrading build farm vms
-u/--upgrade simply upgrades the build farm vms and then exits
-n/--no-upgrade tells autobuild to skip upgrading build farm vms
before building your packages
* Added locale generation to initrd preseed in build farm VMs
This removes the possibility for dubious locale warnings in the build
farm VMs which would otherwise pollute our build logs
* Generate job id for builds before starting
Previously, the builds were saved in /var/autobuild/builds/PID
Generating a job ID based on the current timestamp + the PID reduces
the chance for collisions in the event that the builds directory
hasn't been cleaned
* Added -L option to redirect output to a specified log file or folder
-- rail5 <andrew@rail5.org> Sun, 06 Oct 2024 00:22:52 +0800
autobuild (3.1.7) stable; urgency=medium
* Copy config file to /var/autobuild on new installs in -s
-- rail5 <andrew@rail5.org> Sun, 29 Sep 2024 08:27:23 +0800
autobuild (3.1.6) stable; urgency=medium
* Added config file editor to autobuild -s
-- rail5 <andrew@rail5.org> Sun, 22 Sep 2024 14:49:08 +0800
autobuild (3.1.5) stable; urgency=medium
* Enable contents listing by default to work with apt-file
-- rail5 <andrew@rail5.org> Sun, 08 Sep 2024 16:48:01 +0800
autobuild (3.1.4) stable; urgency=medium
* Allow user to pull from private repos
If the user has provided the necessary credentials in their config
file, there's no reason that autobuild shouldn't permit them to
pull packages from private repos
-- rail5 <andrew@rail5.org> Sun, 08 Sep 2024 12:24:31 +0800
autobuild (3.1.3) stable; urgency=medium
* Fixed possible trouble with boot order when installing arm64 vm
* Fixed stateless builds on arm64
Through a mindless and embarrassing oversight, the arm64 vm was
not run in stateless mode. It is now
* Correctly parsing changelogs which contain asterisks
-- rail5 <andrew@rail5.org> Tue, 03 Sep 2024 18:21:17 +0800
autobuild (3.1.2) stable; urgency=medium
* Forcefully set 755 on /var/autobuild. This should fix autobuild on
Ubuntu
-- rail5 <andrew@rail5.org> Fri, 23 Aug 2024 11:34:41 +0800
autobuild (3.1.1) stable; urgency=medium
* Output daemon errors to the socket until we're more production-ready
Cover for pushing to Debian repo if source package was a tar archive
Help the daemon find fresh repo directories
-- rail5 <andrew@rail5.org> Thu, 18 Jul 2024 16:07:43 +0800
autobuild (3.1.0) stable; urgency=medium
* Autobuild now creates a new Debian repo for the user, rather than
relying on the user already owning one.
This repo can be shipped either as a GitHub Pages
site, or via some web server on the host machine, etc. We just
create the files.
-- rail5 <andrew@rail5.org> Sat, 01 Jun 2024 16:36:02 +0800
autobuild (3.0.2) stable; urgency=medium
* Patched some bugs with v3 debian repo pushing. Github email now must
be stored in config file, unfortunately. This, the username, and the
access token are necessary to push changes -- previously, this
wasn't a problem, when autobuild was run as the user (rather than as
the _autobuild system user), because we could assume that the user
had their git configured properly. But now that we're operating out
of /var/autobuild with a system user distinct from the normal user,
we need to handle this ourselves. In the meantime, reprepro was
complaining about being unable to find .tar.xz files which were
necessary to publish changes. I'm not sure if reprepro changed, in
that it didn't require these before, or if debuild changed, in that
it used to produce .tar.gz files instead of .tar.xz files, or
neither changed but something else happened. In either case,
autobuild now saves tar archives of ANY kind, where before it used
to only save .tar.gz files, from debuild.
-- rail5 <andrew@rail5.org> Fri, 31 May 2024 17:14:04 +0800
autobuild (3.0.1) stable; urgency=medium
* Removed (now unneeded) dependency and updated manual
-- rail5 <andrew@rail5.org> Fri, 31 May 2024 16:14:28 +0800
autobuild (3.0.0) stable; urgency=medium
* Version 3 release

13
debian/control vendored
View file

@ -11,6 +11,17 @@ License: AGPL-3.0
Vendor: rail5 <andrew@rail5.org>
Priority: optional
Architecture: all
Depends: sudo, xdg-utils, pulseaudio-utils, build-essential, make, git, wget, curl, devscripts, xz-utils, jq, reserialize, reprepro, sshpass, libarchive-tools, syslinux, syslinux-utils, cpio, genisoimage, qemu-system, qemu-utils, net-tools, dialog, socat, adduser, ${misc:Depends}
Depends: sudo, pulseaudio-utils, build-essential, make, git, curl, devscripts, gpg, xz-utils, jq, reserialize, reprepro, sshpass, libarchive-tools, syslinux, syslinux-utils, cpio, genisoimage, qemu-system, qemu-utils, net-tools, dialog, socat, adduser, ${misc:Depends}
Description: Automatically build & distribute multi-arch Debian packages from source
Autobuild builds Debian packages from Git sources and distributes them
Package: autobuild-web
Version: 1.0
License: AGPL-3.0
Vendor: rail5 <andrew@rail5.org>
Priority: optional
Architecture: all
Depends: autobuild, reserialize, gpg, php-fpm, php-sqlite3, php-gregwar-captcha, ${misc:Depends}
Recommends: apache2 | nginx
Description: Web-based front-end for autobuild
Web-based UI to configure and run autobuild

26
debian/postinst vendored
View file

@ -1,26 +0,0 @@
#!/bin/bash
set -e
# Create the autobuild user
if ! id -u _autobuild; then
adduser --system --home /var/autobuild _autobuild
# Make sure the autobuild user has permission to run VMs with KVM acceleration
if [[ $(getent group kvm) ]]; then
usermod -aG kvm _autobuild
fi
fi
# Create the /var/autobuild directory
if [[ ! -d /var/autobuild ]]; then
mkdir -p /var/autobuild
fi
# Make sure the autobuild user owns the /var/autobuild directory
chown _autobuild /var/autobuild
# Enable and start the autobuild service
deb-systemd-invoke enable autobuild.socket
deb-systemd-invoke start autobuild.socket
#DEBHELPER#

10
debian/rules vendored
View file

@ -5,9 +5,19 @@
override_dh_auto_install:
pandoc --standalone --to man "$$(pwd)/manual/autobuild.1.md" -o "$$(pwd)/debian/autobuild.1"
mkdir -p "$$(pwd)/debian/autobuild/usr/share/autobuild"
mkdir -p "$$(pwd)/debian/autobuild/usr/share/autobuild/repository"
cp config.toml "$$(pwd)/debian/autobuild/usr/share/autobuild/config.toml"
cp -r build-farm "$$(pwd)/debian/autobuild/usr/share/autobuild/build-farm"
cp bell.ogg "$$(pwd)/debian/autobuild/usr/share/autobuild/bell.ogg"
cp repository/default-index.html "$$(pwd)/debian/autobuild/usr/share/autobuild/repository/default-index.html"
install -D -m 0755 autobuildd "$$(pwd)/debian/autobuild/usr/bin/autobuildd"
install -D -m 0755 autobuild "$$(pwd)/debian/autobuild/usr/bin/autobuild"
install -D -m 0755 autobuild-setup "$$(pwd)/debian/autobuild/usr/bin/autobuild-setup"
mkdir -p "$$(pwd)/debian/autobuild-web/usr/share/autobuild-web/autobuild"
cp -r web/* "$$(pwd)/debian/autobuild-web/usr/share/autobuild-web/autobuild/"
mkdir -p "$$(pwd)/debian/autobuild-web/usr/share/autobuild-web/php-fpm"
mkdir -p "$$(pwd)/debian/autobuild-web/usr/share/autobuild-web/apache"
mkdir -p "$$(pwd)/debian/autobuild-web/usr/share/autobuild-web/nginx"
cp "webconf/php-fpm/autobuild-web.conf" "$$(pwd)/debian/autobuild-web/usr/share/autobuild-web/php-fpm/autobuild-web.conf"
cp "webconf/apache/autobuild-web.conf" "$$(pwd)/debian/autobuild-web/usr/share/autobuild-web/apache/autobuild-web.conf"
cp "webconf/nginx/autobuild-web.conf" "$$(pwd)/debian/autobuild-web/usr/share/autobuild-web/nginx/autobuild-web.conf"

View file

@ -7,12 +7,12 @@ autobuild \- Automatically build and distribute Debian packages
`autobuild --amd64 --github-page -p https://github.com/user/package.git`
`autobuild --i386 --debian-repo --forgejo-page -p /path/to/package.tar.gz`
`autobuild --i386 --debian-repo default --forgejo-page -p /path/to/package.tar.gz`
# DESCRIPTION
Autobuild automatically retrieves Debian source packages (via Git or local tar.gz archive), builds them on a virtual machine build-farm for multiple architectures, and distributes them. It can save the built package locally, distribute them to a GitHub/Forgejo Release Page, and distribute them to a Git-based Debian Repository with reprepro.
The configuration file can be found at /var/autobuild/config.toml. You may edit this file to change various settings.
The configuration file can be found at /var/autobuild/config.toml. This file may be more conveniently edited via the autobuild -s command.
# OPTIONS
@ -20,16 +20,14 @@ The configuration file can be found at /var/autobuild/config.toml. You may edit
-p, \--package
Add a package to the build list. The argument can be:
- The name of a package in the config file
- A valid Git URL
- A local path to a .tar.gz archive
-o, \--output
Specify directory to save built package files (default: current working directory)
Specify directory to save built package files
(default: current working directory)
-b, \--bell
@ -39,17 +37,25 @@ The configuration file can be found at /var/autobuild/config.toml. You may edit
List packages present in the config file and quit
-L, \--log
Write output to a specified log directory instead of to the
terminal.
Autobuild will create a file in that directory called {JOB-ID}.log
Individual package build logs will also be written to that directory
instead of to /var/autobuild/log.
-r, \--remove-old-builds
Remove a given subdirectory under /var/autobuild/builds, and quit
If the given argument is 'all', remove everything under /var/autobuild/builds
If the given argument is 'all', then remove everything under
/var/autobuild/builds
-s, \--setup
Run the setup program (must be run as root)
The setup program can automatically install (or reinstall) the build farm
The setup program can automatically install (or reinstall) the
build farm
## BUILD FARM OPTIONS
-0, \--local
@ -68,24 +74,50 @@ The configuration file can be found at /var/autobuild/config.toml. You may edit
Build packages on the arm64 build farm VM
-u, \--upgrade
Upgrade Build Farm VMs and exit
If --amd64, --i386, and/or --arm64 are specified, upgrade only those VMs which were specified
-n, \--no-upgrade
Do not upgrade Build Farm VMs before building packages
## DISTRIBUTION OPTIONS
-g, \--github-page
Create release pages for the built packages' GitHub repositories
Your GitHub credentials must be stored in the config file
-f, \--forgejo-page
Create release pages for the built packages' Forgejo repositories
Your Forgejo credentials (and instance URL) must be stored in the config file
Your Forgejo credentials (and instance URL) must be stored in the
config file
-d, \--debian-repo
Distribute built packages to a Git-based Debian Repository via reprepro
Distribute built packages to a Git-based Debian Repository via
reprepro
This repository must be configured via the config file
The argument should be the name of a Debian repo to push to
## SIGNING KEY OPTIONS
-C, \--create-signing-key
Create a new package signing key
-D, \--delete-signing-key
Delete an existing package signing key
-E, \--key-email
Specify email address to use for the signing key
-N, \--key-name
Specify name to use for the signing key
# AUTHOR
rail5 (andrew@rail5.org)

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head>
<title>Autobuild</title>
<style>
.container {
display: inline-block;
border: 1px solid black;
background-color: #212121;
color: #ffffff;
}
</style>
</head>
<body>
<h1>Welcome to Autobuild!</h1>
If you see this page, Autobuild is configured to serve a Debian Repository. This default page can be replaced.
<br>
<br>
Users can configure Debian to use this repository as follows:
<br>
<div class="container">
<pre>
sudo curl -s -o /etc/apt/trusted.gpg.d/%REPO_FRIENDLYNAME%-signing-key.gpg "%REPO_URL%/%REPO_FRIENDLYNAME%-signing-key.gpg"
sudo curl -s -o /etc/apt/sources.list.d/%REPO_FRIENDLYNAME%.list "%REPO_URL%/%REPO_FRIENDLYNAME%.list"
sudo apt update
</pre>
</div>
</body>
</html>

View file

@ -10,5 +10,6 @@ ExecStart=/usr/bin/autobuildd
ExecReload=/bin/kill -HUP $MAINPID
StandardInput=socket
StandardOutput=socket
StandardError=journal
# StandardError=journal
StandardError=socket
MountFlags=slave

255
web/build.php Normal file
View file

@ -0,0 +1,255 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
$settings = parse_config();
$debian_repos = get_debian_repos();
$debian_repos_configured = !empty($debian_repos);
$github_configured = github_is_configured($settings);
$forgejo_configured = forgejo_is_configured($settings);
$amd64_configured = vm_is_configured("amd64");
$i386_configured = vm_is_configured("i386");
$arm64_configured = vm_is_configured("arm64");
if (isset($_GET['submitted']) && !isset($_GET['error'])) {
/* Form submitted */
// Do we have everything we need?
if (!isset($_GET["pkg"])) {
$_GET["error"] = "no-pkg";
redirect_and_die("build.php", $_GET);
}
if (!isset($_GET["arch"])) {
$_GET["error"] = "no-arch";
redirect_and_die("build.php", $_GET);
}
$packages = array(); // Packages we want to build
$architectures = array(); // Architectures we want to build them for
$distribution_channels = array(); // Places we want to distribute those packages
$repos_to_push = array(); // Debian repos we want to push them to (if pushing to debian repos)
$upgrade_vms = isset($_GET["upgrade"]); // Do we want to upgrade the VMs before building?
// First, validate that all of the packages are alright
$acceptable_packages = $settings["packages"]["package_urls"];
for ($i = 0; $i < count($acceptable_packages); $i++) {
$acceptable_packages[$i] = str_replace(".git", "", basename($acceptable_packages[$i]));
}
/* Parse options */
// Packages
foreach ($_GET['pkg'] as $package) {
if (in_array($package, $acceptable_packages)) {
$packages[] = $package;
}
}
// Architectures
foreach ($_GET["arch"] as $architecture) {
$valid_arch = false;
switch ($architecture) {
case "amd64":
$valid_arch = $amd64_configured;
break;
case "i386":
$valid_arch = $i386_configured;
break;
case "arm64":
$valid_arch = $arm64_configured;
break;
}
if ($valid_arch && !in_array($architecture, $architectures)) {
$architectures[] = $architecture;
}
}
// Distribution Channels
foreach ($_GET["dist"] as $distribution_channel) {
$valid_channel = false;
switch ($distribution_channel) {
case "debian-repo":
$valid_channel = true;
break;
case "github":
$valid_channel = true;
break;
case "forgejo":
$valid_channel = true;
break;
}
if ($valid_channel && !in_array($distribution_channel, $distribution_channels)) {
$distribution_channels[] = $distribution_channel;
}
}
// If we're distributing to Debian repos, which ones?
if (in_array("debian-repo", $distribution_channels)) {
foreach ($_GET["repo"] as $repository) {
if (in_array($repository, $debian_repos) && !in_array($repository, $repos_to_push)) {
$repos_to_push[] = $repository;
}
}
}
/* Generate build command */
$build_command = "";
foreach ($packages as $package) {
$build_command .= "-p \"$package\" ";
}
foreach ($architectures as $arch) {
$build_command .= "--$arch ";
}
foreach ($distribution_channels as $distribution_channel) {
switch ($distribution_channel) {
case "github":
$build_command .= "-g ";
break;
case "forgejo":
$build_command .= "-f ";
break;
case "debian-repo":
foreach ($repos_to_push as $repository) {
$build_command .= "-d \"$repository\" ";
}
break;
}
}
if (!$upgrade_vms) {
$build_command .= "-n ";
}
$build_command .= "-L \"/var/autobuild/web/log\" ";
/* Run build command */
$autobuild_jobid = run_autobuild_and_get_jobid($build_command);
// Get the PID
$autobuild_pid = substr($autobuild_jobid, strrpos($autobuild_jobid, "."));
// Get the timestamp
$timestamp = str_replace("$autobuild_pid", "", $autobuild_jobid);
$timestamp = str_replace(".", " ", $timestamp);
$timestamp = preg_replace('/[^0-9\-\ :(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)]/', "", $timestamp);
add_build($timestamp);
// Redirect to watch page
header("location: log.php?log=$autobuild_jobid");
die();
}
$debian_checkbox_enabled = $debian_repos_configured ? "" : " disabled";
$github_checkbox_enabled = $github_configured ? "" : " disabled";
$forgejo_checkbox_enabled = $forgejo_configured ? "" : " disabled";
$amd64_checkbox_enabled = $amd64_configured ? "" : " disabled";
$i386_checkbox_enabled = $i386_configured ? "" : " disabled";
$arm64_checkbox_enabled = $arm64_configured ? "" : " disabled";
display_header();
display_error_message();
?>
<main>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<div class="card" id="new-build">
<h2>Start New Build</h2>
<form action="build.php" method="get">
<h3><u>Packages</u></h3>
<div class="checkbox-list">
<?php
foreach ($settings["packages"]["package_urls"] as $index => $url ) {
$package_name = str_replace(".git", "", basename($url));
$pkg_checkbox_onoff = isset($_GET["pkg"]) && in_array($package_name, $_GET["pkg"]) ? " checked" : "";
echo "<li><input type=\"checkbox\" id=\"$package_name\" name=\"pkg[]\" value=\"$package_name\"$pkg_checkbox_onoff>";
echo "<label for=\"$package_name\"> $package_name</label></li>".PHP_EOL;
}
$amd64_checkbox_onoff = isset($_GET["arch"]) && in_array("amd64", $_GET["arch"]) ? " checked":"";
$i386_checkbox_onoff = isset($_GET["arch"]) && in_array("i386", $_GET["arch"]) ? " checked":"";
$arm64_checkbox_onoff = isset($_GET["arch"]) && in_array("arm64", $_GET["arch"]) ? " checked":"";
?>
</div>
<h3><u>Target Architectures</u></h3>
<div class="checkbox-list">
<li>
<input type="checkbox" id="amd64" name="arch[]" value="amd64"<?php echo $amd64_checkbox_onoff.$amd64_checkbox_enabled; ?>>
<label for="amd64">amd64</label>
</li>
<li>
<input type="checkbox" id="i386" name="arch[]" value="i386"<?php echo $i386_checkbox_onoff.$i386_checkbox_enabled; ?>>
<label for="i386">i386</label>
</li>
<li>
<input type="checkbox" id="arm64" name="arch[]" value="arm64"<?php echo $arm64_checkbox_onoff.$arm64_checkbox_enabled; ?>>
<label for="arm64">arm64</label>
</li>
</div>
<h3><u>Distribution Channels</u></h3>
<div class="two-by-two">
<input type="checkbox" id="debian-repo" name="dist[]" value="debian-repo" class="unhide"<?php echo $debian_checkbox_enabled; ?>>
<label for="debian-repo">Debian Repositories</label>
<div class="hidden">
<br>
<?php
foreach ($debian_repos as $repo_name ) {
echo "<li><input type=\"checkbox\" id=\"$repo_name\" name=\"repo[]\" value=\"$repo_name\">";
echo "<label for=\"$repo_name\"> $repo_name</label></li>".PHP_EOL;
}
?>
</div>
</div>
<li>
<input type="checkbox" id="github" name="dist[]" value="github"<?php echo $github_checkbox_enabled ?>>
<label for="github">GitHub Release Pages</label>
</li>
<li>
<input type="checkbox" id="forgejo" name="dist[]" value="forgejo"<?php echo $forgejo_checkbox_enabled ?>>
<label for="forgejo">Forgejo Release Pages</label>
</li>
<h3><u>Options</u></h3>
<li>
<input type="checkbox" id="upgrade" name="upgrade" value="1">
<label for="upgrade">Upgrade Build Farm VMs before building</label>
</li>
<input type="hidden" name="submitted" value="true">
<button type="submit">Start Build</button>
</form>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

25
web/captcha.php Normal file
View file

@ -0,0 +1,25 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
session_start();
error_reporting(0);
ini_set('display_errors', 0);
ini_set('display_startup_errors', 0);
require_once "/usr/share/php/Gregwar/Captcha/autoload.php";
use Gregwar\Captcha\CaptchaBuilder;
$builder = new CaptchaBuilder;
$builder->build();
$_SESSION['captcha'] = $builder->getPhrase();
header('Content-Type: image/jpeg');
$builder->output();

60
web/download.php Normal file
View file

@ -0,0 +1,60 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
$autobuild_directory = "/var/autobuild";
$autobuild_builds_directory = "$autobuild_directory/builds";
$error_params = array( "error" => "invalid-file");
if (!isset($_GET["jobid"]) || !isset($_GET["pkg"]) || !isset($_GET["file"])) {
redirect_and_die("index.php");
}
$jobid = $_GET["jobid"];
$pkg = $_GET["pkg"];
$file = $_GET["file"];
/* Validate the info */
// Do we have files for the given Job ID?
$job_path = "$autobuild_builds_directory/$jobid";
$valid_jobid = file_exists($job_path)
&& dirname(realpath($job_path)) == $autobuild_builds_directory;
if (!$valid_jobid) {
redirect_and_die("index.php", $error_params);
}
// Was this package among the ones built for this Job ID?
$pkg_path = "$job_path/$pkg";
$valid_pkg = file_exists($pkg_path)
&& dirname(realpath($pkg_path)) == $job_path;
if (!$valid_pkg) {
redirect_and_die("index.php", $error_params);
}
// Was this file built?
$file_path = "$pkg_path/$file";
$valid_file = file_exists($file_path)
&& dirname(realpath($file_path)) == $pkg_path;
if (!$valid_file) {
redirect_and_die("index.php", $error_params);
}
// Finally, give them the file:
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"$file\"");
readfile($file_path);

501
web/global.config.php Normal file
View file

@ -0,0 +1,501 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
/* Config access functions */
$config_file = "/var/autobuild/config.toml";
$build_farm_directory = "/var/autobuild/build-farm";
$repository_directory = "/var/autobuild/repo";
function parse_config() {
global $config_file;
$shell_escaped_config_file = escapeshellarg($config_file);
$parsed = json_decode(`toml2json $shell_escaped_config_file`, true);
return $parsed;
}
function update_config($new_config) {
global $config_file;
$new_config_contents = "# Autobuild configuration";
$new_config_contents .= PHP_EOL.PHP_EOL;
$new_config_contents .= "[packages]";
$new_config_contents .= PHP_EOL;
$new_config_contents .= "package_urls = [".PHP_EOL;
/* Parse packages */
for ($i = 0; $i < count($new_config["packages"]) - 1; $i++) {
$new_config_contents .= "\t".'"'.$new_config["packages"][$i].'",'.PHP_EOL;
}
$new_config_contents .= "\t".'"'.$new_config["packages"][count($new_config["packages"]) - 1].'"'.PHP_EOL;
$new_config_contents .= "]";
$new_config_contents .= PHP_EOL.PHP_EOL;
/* Parse GitHub settings */
$new_config_contents .= "[github]".PHP_EOL;
$new_config_contents .= "# Owner username".PHP_EOL;
$new_config_contents .= 'repo_owner = "'.$new_config["github"]["repo_owner"].'"'.PHP_EOL.PHP_EOL;
$new_config_contents .= "# Github Email".PHP_EOL;
$new_config_contents .= 'email = "'.$new_config["github"]["email"].'"'.PHP_EOL;
$new_config_contents .= "# Access token".PHP_EOL;
$new_config_contents .= 'access_token = "'.$new_config["github"]["access_token"].'"'.PHP_EOL;
$new_config_contents .= PHP_EOL;
/* Parse Forgejo settings */
$new_config_contents .= "[forgejo]".PHP_EOL;
$new_config_contents .= "# The distribution settings assume that the repository names are the same across github/forgejo/etc".PHP_EOL;
$new_config_contents .= PHP_EOL;
$new_config_contents .= "# Location of your forgejo instance".PHP_EOL;
$new_config_contents .= 'instance_url = "'.$new_config["forgejo"]["instance_url"].'"'.PHP_EOL;
$new_config_contents .= PHP_EOL;
$new_config_contents .= "# Owner username".PHP_EOL;
$new_config_contents .= 'repo_owner = "'.$new_config["forgejo"]["repo_owner"].'"'.PHP_EOL;
$new_config_contents .= PHP_EOL;
$new_config_contents .= "# Access token".PHP_EOL;
$new_config_contents .= 'access_token = "'.$new_config["forgejo"]["access_token"].'"'.PHP_EOL;
file_put_contents($config_file, $new_config_contents);
}
function github_is_configured($config_data = 0) {
if ($config_data == 0) {
$config_data = parse_config();
}
return isset($config_data["github"]["repo_owner"])
&& isset($config_data["github"]["email"])
&& isset($config_data["github"]["access_token"]);
}
function forgejo_is_configured($config_data = 0) {
if ($config_data == 0) {
$config_data = parse_config();
}
return isset($config_data["forgejo"]["instance_url"])
&& isset($config_data["forgejo"]["repo_owner"])
&& isset($config_data["forgejo"]["access_token"]);
}
function vm_is_configured($arch) {
global $build_farm_directory;
if (vm_is_installing($arch)) {
return false;
}
$file_to_check = "$build_farm_directory/";
switch ($arch) {
case "amd64":
$file_to_check .= "debian-stable-amd64/";
break;
case "i386":
$file_to_check .= "debian-stable-i386/";
break;
case "arm64":
$file_to_check .= "debian-stable-arm64/";
break;
}
$file_to_check .= "image.qcow";
return file_exists($file_to_check);
}
function vm_is_installing($arch) {
global $build_farm_directory;
$file_to_check = "$build_farm_directory/";
switch ($arch) {
case "amd64":
$file_to_check .= "debian-stable-amd64/";
break;
case "i386":
$file_to_check .= "debian-stable-i386/";
break;
case "arm64":
$file_to_check .= "debian-stable-arm64/";
break;
}
$file_to_check .= "installing";
return file_exists($file_to_check);
}
function vm_is_upgrading($arch) {
global $build_farm_directory;
$file_to_check = "$build_farm_directory/";
switch ($arch) {
case "amd64":
$file_to_check .= "debian-stable-amd64/";
break;
case "i386":
$file_to_check .= "debian-stable-i386/";
break;
case "arm64":
$file_to_check .= "debian-stable-arm64/";
break;
}
$file_to_check .= "upgrading";
return file_exists($file_to_check);
}
function vm_upgrade($arch_list, $redirect = true) {
global $build_farm_directory;
foreach ($arch_list as $arch) {
if (vm_is_upgrading($arch) || !vm_is_configured($arch)) {
continue;
}
run_autobuild("--$arch -u");
}
if ($redirect) {
redirect_and_die("back", array("note" => "upgrading-vm"));
}
}
function vm_install($arch_list) {
global $build_farm_directory;
foreach ($arch_list as $arch) {
if (vm_is_installing($arch) || vm_is_configured($arch) || vm_is_upgrading($arch)) {
continue;
}
run_autobuild("--$arch -i");
}
redirect_and_die("back", array("note" => "installing-vm"));
}
function vm_uninstall($arch_list) {
global $build_farm_directory;
foreach ($arch_list as $arch) {
if (!vm_is_configured($arch) || vm_is_installing($arch) || vm_is_upgrading($arch)) {
continue;
}
$arch_directory = "$build_farm_directory/";
switch ($arch) {
case "amd64":
$arch_directory .= "debian-stable-amd64/";
break;
case "i386":
$arch_directory .= "debian-stable-i386/";
break;
case "arm64":
$arch_directory .= "debian-stable-arm64/";
break;
}
unlink($arch_directory."image.qcow");
unlink($arch_directory."preseed.cfg");
redirect_and_die("back", array("note" => "removed-vm"));
}
}
function get_upgrades_to_run($older_than) {
global $build_farm_directory;
$arch_list = array("amd64", "i386", "arm64");
$upgrades = array();
foreach ($arch_list as $arch) {
if (vm_is_upgrading($arch) || !vm_is_configured($arch)) {
continue;
}
$arch_directory = "$build_farm_directory/";
switch ($arch) {
case "amd64":
$arch_directory .= "debian-stable-amd64/";
break;
case "i386":
$arch_directory .= "debian-stable-i386/";
break;
case "arm64":
$arch_directory .= "debian-stable-arm64/";
break;
}
$last_upgrade = filemtime($arch_directory."image.qcow");
if ((time() - $last_upgrade) > ($older_than * 60)) {
$upgrades[] = $arch;
}
}
return $upgrades;
}
function get_debian_repos() {
global $repository_directory;
$repo_folders = array_filter(glob('/var/autobuild/repo/*'), 'is_dir');
$debian_repos = array();
foreach ($repo_folders as $repo_folder) {
if (file_exists($repo_folder."/autobuild_repo.conf")) {
$debian_repos[] = basename($repo_folder);
}
}
return $debian_repos;
}
function create_debian_repo($repo_name, $repo_url, $signing_key, $github_pages, $github_pages_url) {
global $repository_directory;
if ($github_pages && !github_is_configured()) {
$_GET["error"] = "github-not-configured";
redirect_and_die("back", $_GET);
}
$debian_repos = get_debian_repos();
if (in_array($repo_name, $debian_repos)) {
$_GET["error"] = "repo-exists";
redirect_and_die("back", $_GET);
}
$repo_folder = $repository_directory."/".$repo_name;
$signing_key_fingerprint = get_signing_key_fingerprint($signing_key);
// Create the repository folder
mkdir($repo_folder, 0755);
if ($github_pages) {
// Prepare the GitHub Pages repository
$config = parse_config();
$escaped_repo_folder = escapeshellarg($repo_folder);
$escaped_github_email = escapeshellarg($config["github"]["email"]);
$escaped_github_username = escapeshellarg($config["github"]["repo_owner"]);
$escaped_github_url = escapeshellarg($github_pages_url);
$pushpull_url = str_replace("://", "://".$config["github"]["repo_owner"].":".$config["github"]["access_token"]."@", $github_pages_url);
$escaped_pushpull_url = escapeshellarg($pushpull_url);
`cd $escaped_repo_folder; git init; git config user.email $escaped_github_email; git config user.name $escaped_github_username; git remote add origin $escaped_github_url; git pull $escaped_pushpull_url -q`;
}
mkdir("$repo_folder/conf", 0755);
// Prepare the new repository for reprepro
$conf_distributions_content = "Origin: $repo_name".PHP_EOL;
$conf_distributions_content .= "Label: $repo_name".PHP_EOL;
$conf_distributions_content .= "Codename: unstable".PHP_EOL;
$conf_distributions_content .= "Architectures: source amd64 i386 arm64".PHP_EOL;
$conf_distributions_content .= "Components: main".PHP_EOL;
$conf_distributions_content .= "Description: $repo_name".PHP_EOL;
$conf_distributions_content .= "SignWith: $signing_key_fingerprint".PHP_EOL;
$conf_distributions_content .= "Contents: .gz".PHP_EOL;
file_put_contents($repo_folder."/conf/distributions", $conf_distributions_content);
// Export the public key
$public_key_file = "$repo_folder/$repo_name-signing-key.gpg";
$escaped_public_key_file = escapeshellarg($public_key_file);
`gpg --export $signing_key_fingerprint > $escaped_public_key_file`;
// Create the repo .list file
$list_file_contents = "deb $repo_url unstable main".PHP_EOL;
$list_file_contents .= "deb-src $repo_url unstable main".PHP_EOL;
file_put_contents("$repo_folder/$repo_name.list", $list_file_contents);
// Create the default index.html file for the repo from our template
$default_index_html_contents = file_get_contents("/usr/share/autobuild/repository/default-index.html");
$default_index_html_contents = str_replace("%REPO_FRIENDLYNAME%", $repo_name, $default_index_html_contents);
$default_index_html_contents = str_replace("%REPO_URL%", $repo_url, $default_index_html_contents);
file_put_contents("$repo_folder/index.html", $default_index_html_contents);
// Create the autobuild_repo.conf file
$repo_conf_contents = "[repo]".PHP_EOL;
if ($github_pages) {
$repo_conf_contents .= "ghpages = true".PHP_EOL;
$repo_conf_contents .= "ghpages_url = \"$github_pages_url\"".PHP_EOL;
} else {
$repo_conf_contents .= "ghpages = false".PHP_EOL;
}
file_put_contents("$repo_folder/autobuild_repo.conf", $repo_conf_contents);
if ($github_pages) {
// Push changes to the GitHub Pages repository
`cd $escaped_repo_folder; git pull $escaped_pushpull_url -q; git add --all; git commit -m "Initialized Autobuild GitHub Pages Debian Repository"; git branch -M main`;
`cd $escaped_repo_folder; git push $escaped_pushpull_url --all`;
}
}
function delete_debian_repo($repo) {
global $repository_directory;
$valid_repos = get_debian_repos();
if (!in_array($repo, $valid_repos)) {
redirect_and_die("back", array("error" => "invalid-repo"));
}
remove_directory("$repository_directory/$repo");
}
function delete_debian_repos($repo_list) {
$valid_repos = get_debian_repos();
foreach ($repo_list as $repo) {
if (!in_array($repo, $valid_repos)) {
redirect_and_die("back", array("error" => "invalid-repo"));
}
}
foreach ($repo_list as $repo) {
delete_debian_repo($repo);
}
}
function get_signing_keys($field = "all") {
$keys = `gpg --list-secret-keys --with-colons | awk -F: '$1=="uid" {print $10}'`;
if (empty($keys)) {
return array();
}
$keys = explode(PHP_EOL, $keys);
$key_data = array();
$i = 0;
foreach ($keys as $key) {
if (empty($key)) {
continue;
}
$name = htmlentities(substr($key, 0, strpos($key,"<") - 1));
$email = strtolower(htmlentities(substr($key, strpos($key,"<") + 1, -1)));
switch ($field) {
case "all":
$key_data[$i]["name"] = $name;
$key_data[$i]["email"] = $email;
break;
case "name":
$key_data[$i] = $name;
break;
case "email":
$key_data[$i] = $email;
break;
}
$i++;
}
return $key_data;
}
function get_signing_key_fingerprint($email) {
$signing_keys = get_signing_keys("email");
if (!in_array(strtolower($email), $signing_keys)) {
return "";
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return "";
}
$escaped_email = escapeshellarg($email);
// Now get the key fingerprint and return it
$fingerprint = trim(`gpg -K --with-colons $escaped_email | awk -F: '$1=="fpr" {print $10}' | head -n 1`);
return $fingerprint;
}
function create_signing_key($name, $email) {
$signing_keys = get_signing_keys("email");
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$_GET["error"] = "invalid-email";
redirect_and_die("back", $_GET);
}
if (in_array(strtolower($email), $signing_keys)) {
$_GET["error"] = "key-exists";
redirect_and_die("back", $_GET);
}
$escaped_email = escapeshellarg(filter_var($email, FILTER_SANITIZE_EMAIL));
$escaped_name = escapeshellarg(preg_replace("/[^a-zA-Z0-9\ \-\_\.]/", "", $name));
run_autobuild_and_wait_for_finish("-C -E $escaped_email -N $escaped_name");
}
function delete_signing_key($email) {
$signing_keys = get_signing_keys("email");
if (!in_array(strtolower($email), $signing_keys)) {
redirect_and_die("back", array("error" => "invalid-key"));
}
if (signing_key_is_in_use($email)) {
redirect_and_die("back", array("error" => "key-in-use"));
}
$escaped_email = escapeshellarg($email);
run_autobuild_and_wait_for_finish("-D -E $escaped_email");
}
function signing_key_is_in_use($email) {
$signing_keys = get_signing_keys("email");
if (!in_array(strtolower($email), $signing_keys)) {
return false;
}
$debian_repos = get_debian_repos();
$key_fingerprint = get_signing_key_fingerprint($email);
foreach ($debian_repos as $repo) {
$repo_conf = file_get_contents("/var/autobuild/repo/$repo/conf/distributions");
if (strpos($repo_conf, $key_fingerprint) !== false) {
return true;
}
}
return false;
}
function get_repos_which_use_signing_key($email) {
$signing_keys = get_signing_keys("email");
if (!in_array(strtolower($email), $signing_keys)) {
return array();
}
$debian_repos = get_debian_repos();
$key_fingerprint = get_signing_key_fingerprint($email);
$repos = array();
foreach ($debian_repos as $repo) {
$repo_conf = file_get_contents("/var/autobuild/repo/$repo/conf/distributions");
if (strpos($repo_conf, $key_fingerprint) !== false) {
$repos[] = $repo;
}
}
return $repos;
}

33
web/global.cron.php Normal file
View file

@ -0,0 +1,33 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
/* Cron jobs */
$cron_settings = get_cron_settings();
if ($cron_settings["auto_clear_builds"]) {
$builds_to_clear = get_builds_to_clear($cron_settings["auto_clear_builds_minutes"]);
foreach ($builds_to_clear as $build) {
$build_arg = escapeshellarg($build);
run_autobuild("-r $build_arg");
}
}
if ($cron_settings["auto_clear_logs"]) {
$logs_to_clear = get_logs_to_clear($cron_settings["auto_clear_logs_minutes"]);
foreach ($logs_to_clear as $log) {
delete_log($log);
}
}
if ($cron_settings["auto_upgrade_vms"]) {
$upgrades_to_run = get_upgrades_to_run($cron_settings["auto_upgrade_vms_minutes"]);
vm_upgrade($upgrades_to_run, false);
}

169
web/global.db.php Normal file
View file

@ -0,0 +1,169 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
$db_file = "/var/autobuild/web/db.sqlite";
$db = new SQLite3($db_file) or die("Cannot access SQLite database file");
$db->exec('CREATE TABLE IF NOT EXISTS "data" (
"uid" INTEGER NOT NULL UNIQUE,
"username" TEXT NOT NULL,
"password" TEXT NOT NULL,
"latest_build" TEXT NOT NULL DEFAULT "Never",
"builds" INTEGER NOT NULL DEFAULT 0,
"auto_clear_builds" INTEGER NOT NULL DEFAULT 0,
"auto_clear_builds_minutes" INTEGER NOT NULL DEFAULT 0,
"auto_clear_logs" INTEGER NOT NULL DEFAULT 0,
"auto_clear_logs_minutes" INTEGER NOT NULL DEFAULT 0,
"auto_upgrade_vms" INTEGER NOT NULL DEFAULT 0,
"auto_upgrade_vms_minutes" INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY("uid" AUTOINCREMENT)
);');
bring_db_structure_up_to_date();
$username = $db->query('SELECT username FROM "data" WHERE uid=1')->fetchArray();
if (!$username && basename($_SERVER["PHP_SELF"]) != "setup.php") {
header('location: setup.php');
die();
}
if ($username && basename($_SERVER["PHP_SELF"]) == "setup.php") {
header('location: index.php');
die();
}
if ($username && !$_SESSION['logged-in'] && basename($_SERVER['PHP_SELF']) != "login.php") {
header('location: login.php');
die();
}
/* Database functions */
function bring_db_structure_up_to_date() {
global $db;
// Fields that were not included in the initial release of autobuild-web
$columns = array(
"auto_clear_builds",
"auto_clear_builds_minutes",
"auto_clear_logs",
"auto_clear_logs_minutes",
"auto_upgrade_vms",
"auto_upgrade_vms_minutes"
);
foreach ($columns as $column) {
$test_query = "SELECT INSTR(sql, '$column') FROM sqlite_master WHERE type='table' AND name='data'";
$test_result = $db->query($test_query)->fetchArray();
if ($test_result[0] == 0) {
try {
$db->exec("ALTER TABLE data ADD COLUMN $column INTEGER NOT NULL DEFAULT 0;");
} catch (Exception $e) {
// Do nothing
}
}
}
}
function create_user($username, $password) {
global $db;
$query = $db->prepare('INSERT INTO data (username, password) VALUES (:user, :pass)');
$query->bindValue(':user', $username);
$query->bindValue(':pass', $password);
return $query->execute();
}
function get_username() {
global $db;
return $db->query('SELECT username FROM "data" WHERE uid=1')->fetchArray()[0];
}
function update_user($username, $password) {
global $db;
$query = $db->prepare('UPDATE data SET username = :user, password = :pass WHERE uid=1');
$query->bindValue(':user', $username);
$query->bindValue(':pass', password_hash($password, PASSWORD_DEFAULT));
return $query->execute();
}
function log_in($username, $password) {
global $db;
$credentials = $db->query('SELECT username, password FROM "data" WHERE uid=1')->fetchArray();
return $username == $credentials['username'] && password_verify($password, $credentials['password']);
}
function get_build_stats() {
global $db;
return $db->query('SELECT latest_build, builds FROM "data" WHERE uid=1')->fetchArray();
}
function clear_build_stats() {
global $db;
return $db->query('UPDATE data SET latest_build = "Never", builds = 0 WHERE uid=1')->fetchArray();
}
function get_cron_settings() {
global $db;
return $db->query('SELECT auto_clear_builds, auto_clear_builds_minutes, auto_clear_logs, auto_clear_logs_minutes, auto_upgrade_vms, auto_upgrade_vms_minutes FROM "data" WHERE uid=1')->fetchArray();
}
function update_cron_settings($new_cron) {
global $db;
// Make sure the input values make sense
if ($new_cron['auto_clear_builds'] != 1) {
$new_cron['auto_clear_builds'] = 0;
$new_cron['auto_clear_builds_minutes'] = 0;
}
if ($new_cron['auto_clear_logs'] != 1) {
$new_cron['auto_clear_logs'] = 0;
$new_cron['auto_clear_logs_minutes'] = 0;
}
if ($new_cron['auto_upgrade_vms'] != 1) {
$new_cron['auto_upgrade_vms'] = 0;
$new_cron['auto_upgrade_vms_minutes'] = 0;
}
if ($new_cron['auto_clear_builds'] == 1 && $new_cron['auto_clear_builds_minutes'] < 1) {
$new_cron['auto_clear_builds_minutes'] = 1;
}
if ($new_cron['auto_clear_logs'] == 1 && $new_cron['auto_clear_logs_minutes'] < 1) {
$new_cron['auto_clear_logs_minutes'] = 1;
}
if ($new_cron['auto_upgrade_vms'] == 1 && $new_cron['auto_upgrade_vms_minutes'] < 30) {
$new_cron['auto_upgrade_vms_minutes'] = 30;
}
$query = $db->prepare('UPDATE data SET auto_clear_builds = :acb, auto_clear_builds_minutes = :acbm, auto_clear_logs = :acl, auto_clear_logs_minutes = :aclm, auto_upgrade_vms = :auv, auto_upgrade_vms_minutes = :auvm WHERE uid=1');
$query->bindValue(':acb', $new_cron['auto_clear_builds']);
$query->bindValue(':acbm', $new_cron['auto_clear_builds_minutes']);
$query->bindValue(':acl', $new_cron['auto_clear_logs']);
$query->bindValue(':aclm', $new_cron['auto_clear_logs_minutes']);
$query->bindValue(':auv', $new_cron['auto_upgrade_vms']);
$query->bindValue(':auvm', $new_cron['auto_upgrade_vms_minutes']);
return $query->execute();
}
function add_build($date) {
global $db;
$query = $db->prepare('UPDATE data SET latest_build = :builddate, builds = builds + 1 WHERE uid=1');
$query->bindValue(':builddate', $date);
return $query->execute();
}

180
web/global.display.php Normal file
View file

@ -0,0 +1,180 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
/* Display functions (template html) */
function display_error_message() {
if (isset($_GET["error"])) {
$error_message = "Error";
switch ($_GET["error"]) {
case "no-pkg":
$error_message = "No packages selected";
break;
case "no-arch":
$error_message = "No build architectures selected";
break;
case "invalid-arch":
$error_message = "Invalid build architecture selected";
break;
case "no-log":
$error_message = "No log selected";
break;
case "invalid-log":
$error_message = "Invalid log selection";
break;
case "invalid-file":
$error_message = "Invalid file requested";
break;
case "password-mismatch":
$error_message = "Passwords do not match";
break;
case "invalid-captcha":
$error_message = "Invalid captcha";
break;
case "form-incomplete":
$error_message = "Please fill out all fields";
break;
case "db-error":
$error_message = "Could not contact database";
break;
case "invalid-credentials":
$error_message = "Incorrect username or password";
break;
case "no-repo-name":
$error_message = "Provide a repository name";
break;
case "no-repo-url":
$error_message = "Provide a repository URL";
break;
case "invalid-repo":
$error_message = "Invalid repository";
break;
case "invalid-repo-name":
$error_message = "Invalid repository name";
break;
case "invalid-repo-url":
$error_message = "Invalid repository URL";
break;
case "repo-exists":
$error_message = "Repository already exists";
break;
case "key-exists":
$error_message = "Signing key already exists";
break;
case "invalid-key":
$error_message = "Invalid signing key";
break;
case "key-in-use":
$error_message = "Signing key is currently still in use";
break;
case "invalid-email":
$error_message = "Invalid email address";
break;
case "invalid-github-pages-url":
$error_message = "Invalid GitHub Pages URL";
break;
case "github-not-configured":
$error_message = "Your GitHub credentials are not configured";
break;
case "no-action":
$error_message = "No action specified";
break;
case "invalid-action":
$error_message = "Invalid action specified";
break;
}
echo PHP_EOL.'<div align="center" width="50%" height="50%" class="error-message">Error: '.$error_message.'</div>'.PHP_EOL;
}
}
function display_note() {
if (isset($_GET["note"])) {
$note_message = "Note";
switch ($_GET["note"]) {
case "account-updated":
$note_message = "Account updated";
break;
case "key-added":
$note_message = "Signing key added";
break;
case "key-removed":
$note_message = "Signing key removed";
break;
case "installing-vm":
$note_message = "Installing VMs";
break;
case "upgrading-vm":
$note_message = "Upgrading VMs";
break;
case "removed-vm":
$note_message = "Removed VMs";
break;
}
echo PHP_EOL.'<div align="center" width="50%" height="50%" class="note-message">Note: '.$note_message.'</div>'.PHP_EOL;
}
}
function display_header() {
echo '<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Autobuild</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<div class="container">
<div class="header-content">
<div class="logo"><a href="index.php" class="no-highlight">Autobuild</a></div>
<nav>
<ul>
<li><a href="index.php">Dashboard</a></li>
<li><a href="build.php">Build</a></li>
<li><a href="logs.php">Logs</a></li>
<li><a href="settings.php">Settings</a></li>
<li><a href="repositories.php">Repositories</a></li>
';
if ($_SESSION['logged-in']) {
echo '<li><a href="logout.php">Logout</a></li>';
}
echo '
</ul>
</nav>
</div>
</div>
</header>';
if (!is_secure()) {
echo PHP_EOL.'<div align="center" width="50%" height="50%" class="warning-message">Warning: Your connection is not secure</div>'.PHP_EOL;
}
}
function display_sidebar_actions() {
echo '
<div class="card sidebar">
<h2>Quick Actions</h2>
<ul>
<li><a href="build.php">Start New Build</a></li>
<li><a href="repositories.php">Manage Repositories</a></li>
<li><a href="settings.farm.php">Configure Build Farm</a></li>
</ul>
</div>';
}
function display_sidebar_statistics() {
$build_stats = get_build_stats();
$latest_build = $build_stats['latest_build'];
$total_builds = $build_stats['builds'];
echo "
<div class=\"card sidebar\">
<h2>Build Statistics</h2>
<p>Total Builds: $total_builds</p>
<p>Last Build: $latest_build</p>
</div>";
}

69
web/global.general.php Normal file
View file

@ -0,0 +1,69 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
/* General-purpose functions */
function random_string($length = 32) {
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$charactersLength = strlen($characters);
$randomString = '';
for ($i = 0; $i < $length; $i++) {
$randomString .= $characters[random_int(0, $charactersLength - 1)];
}
return $randomString;
}
function file_not_empty($file) {
return filesize($file) > 0;
}
function redirect_and_die($url, $params = false) {
switch ($url) {
case "back":
$url = strtok($_SERVER['HTTP_REFERER'], "?");
break;
case "self":
$url = (empty($_SERVER['HTTPS']) ? 'http' : 'https') . "://$_SERVER[HTTP_HOST]".parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
break;
}
if ($params) {
$url .= "?".http_build_query($params);
}
header("location: $url");
die();
}
function is_secure() {
return (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| $_SERVER['SERVER_PORT'] == 443
|| $_SERVER['HTTP_HOST'] == "localhost" || $_SERVER['HTTP_HOST'] == "127.0.0.1";
}
function remove_directory($directory) {
if (!file_exists($directory)) {
return true;
}
if (!is_dir($directory)) {
return unlink($directory);
}
foreach (scandir($directory) as $file) {
if ($file == '.'|| $file == '..') {
continue;
}
if (!remove_directory($directory .'/'. $file)) {
return false;
}
}
return rmdir($directory);
}

36
web/global.job.php Normal file
View file

@ -0,0 +1,36 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
$socket_location = "unix:///var/run/autobuild.socket";
/* Functions to run the autobuild daemon */
function run_autobuild($command) {
global $socket_location;
$socket = stream_socket_client($socket_location);
fwrite($socket, $command);
fclose($socket);
}
function run_autobuild_and_get_jobid($command) {
global $socket_location;
$socket = stream_socket_client($socket_location);
fwrite($socket, $command);
fgets($socket); // The first line of output is the PID
$job_id = fgets($socket); // The second line of output is the JOBID
fclose($socket);
$job_id = trim(str_replace("JOBID: ", "", $job_id));
return $job_id;
}
function run_autobuild_and_wait_for_finish($command) {
`autobuild $command`;
}

287
web/global.log.php Normal file
View file

@ -0,0 +1,287 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
/* Logging & log file functions */
$log_directory = "/var/autobuild/web/log";
$autobuild_directory = "/var/autobuild";
$autobuild_repos_directory = "$autobuild_directory/repo";
$autobuild_builds_directory = "$autobuild_directory/builds";
function get_build_logs($offset = 0, $limit = null) {
global $log_directory;
$log_list = array_filter(glob("$log_directory/*.log"), 'file_not_empty');
$log_list = array_slice($log_list, $offset, $limit);
return $log_list;
}
function get_number_of_build_logs() {
global $log_directory;
$log_list = array_filter(glob("$log_directory/*.log"), 'file_not_empty');
return count($log_list);
}
function get_log_file($log_number) {
global $log_directory;
$log_file = "$log_directory/$log_number.log";
$valid_log = file_exists($log_file)
&& dirname(realpath($log_file)) == $log_directory;
if (!$valid_log) {
$_GET["error"] = "invalid-log";
redirect_and_die("back", $_GET);
return "";
}
return $log_file;
}
function get_package_build_logs($log_number) {
global $log_directory;
$package_log_list = glob("$log_directory/$log_number.*.build");
$package_logs = array();
foreach ($package_log_list as $package_log) {
$log_name = basename($package_log, ".build");
$log_name = str_replace("$log_number.", "", $log_name);
$package_logs[$log_name] = $package_log;
}
return $package_logs;
}
function get_package_build_log_names($log_number) {
$package_log_list = get_package_build_logs($log_number);
$package_log_names = array();
foreach ($package_log_list as $package_log) {
$log_name = basename($package_log, ".build");
$log_name = str_replace("$log_number.", "", $log_name);
$package_log_names[] = $log_name;
}
return $package_log_names;
}
function get_package_build_log($log_number, $package) {
$package_log_list = get_package_build_logs($log_number);
foreach ($package_log_list as $name => $package_log) {
if ($name == $package) {
return $package_log;
}
}
return "";
}
function delete_log($log_number) {
global $log_directory;
$log_file = get_log_file($log_number);
$status_file = get_status_file($log_number);
$package_log_files = get_package_build_logs($log_number);
$jobid = escapeshellarg(get_job_jobid($log_number));
unlink($log_file);
unlink($status_file);
foreach ($package_log_files as $package_log_file) {
unlink($package_log_file);
}
run_autobuild("-r $jobid");
}
function get_status_file($log_number) {
global $log_directory;
$status_file = "$log_directory/$log_number.status";
return $status_file;
}
function write_status_file($log_number, $status_code) {
$status_file = get_status_file($log_number);
file_put_contents($status_file, $status_code);
// Make sure the autobuild user can read/write the status file
chmod($status_file, 0770);
return $status_file;
}
function delete_all_logs() {
global $log_directory;
$logs = array_filter(glob("$log_directory/*.{log,status,build}", GLOB_BRACE));
foreach ($logs as $log) {
unlink($log);
}
run_autobuild("-r all");
}
function get_logs_to_clear($older_than) {
global $log_directory;
$logs = array_filter(glob("$log_directory/*.log"), 'file_not_empty');
$logs_to_clear = array();
foreach ($logs as $log) {
$log_number = basename($log, ".log");
$timestamp = filemtime($log);
if ((time() - $timestamp) > ($older_than * 60) && get_job_status($log_number) < 4) {
$logs_to_clear[] = $log_number;
}
}
return $logs_to_clear;
}
function get_job_pid($log_number) {
global $log_directory;
$log_file = escapeshellarg(get_log_file($log_number));
return trim(`head -n 1 $log_file | awk '{print \$2}'`);
}
function get_job_jobid($log_number) {
global $log_directory;
$log_file = escapeshellarg(get_log_file($log_number));
return trim(`sed -n '2{p;q;}' $log_file | awk '{print \$2}'`);
}
function get_job_status($log_number) {
global $log_directory;
global $autobuild_builds_directory;
/***
* 3 bits:
* First bit: Is the job still running?
* Second bit: Did autobuild report success?
* Third bit: Is the job either QUEUED or was it CANCELED?
* From this, here are the status codes:
* 000: (Decimal 0)
* Job failed
* 001: (Decimal 1)
* Job canceled
* 010: (Decimal 2)
* Job completed successfully
* 100: (Decimal 4)
* Job in progress
* 101: (Decimal 5)
* Job queued
* We don't need to care about any other codes
*/
if (file_exists(get_status_file($log_number))) {
return intval(file_get_contents(get_status_file($log_number)));
}
// Get the PID and the Job ID
$pid = get_job_pid($log_number);
$log_file = escapeshellarg(get_log_file($log_number));
$logfile_last_line = trim(`tail -n 1 $log_file`);
$in_progress = 4 * file_exists("/proc/$pid");
$reported_success = 2 * ($logfile_last_line == "Success");
$queued_or_canceled = 1 * (($logfile_last_line == "Queued") || ($logfile_last_line == "Canceled"));
$status_code = $in_progress | $reported_success | $queued_or_canceled;
if (($status_code & 4) == 0) {
// Job is not running anymore -- write a status file
write_status_file($log_number, $status_code);
}
return $status_code;
}
function get_builds_to_clear($older_than) {
global $autobuild_builds_directory;
$builds = array_filter(glob("$autobuild_builds_directory/*"), 'is_dir');
$builds_to_clear = array();
foreach ($builds as $build) {
$timestamp = filemtime("$build/.");
if ((time() - $timestamp) > ($older_than * 60)) {
$builds_to_clear[] = basename($build);
}
}
return $builds_to_clear;
}
function print_status_code($status_code, $html = false) {
$label = "";
$color = "000000";
switch ($status_code) {
case 0:
$label = "Failed";
$color = "FF0000";
break;
case 1:
$label = "Canceled";
$color = "FF0000";
break;
case 2:
$label = "Successful";
$color = "00FF00";
break;
case 4:
$label = "In progress";
$color = "0000FF";
break;
case 5:
$label = "Queued";
$color = "0000FF";
break;
}
if ($html) {
$label = "<font color=\"#$color\">$label</font>";
}
return $label;
}
function get_download_links($log_number) {
global $log_directory;
global $autobuild_builds_directory;
$jobid = get_job_jobid($log_number);
$build_files_directory = "$autobuild_builds_directory/$jobid";
$package_directories = array_filter(glob(pattern: "$build_files_directory/*"), 'is_dir');
$package_debs = array();
foreach ($package_directories as $package_directory) {
foreach (glob("$package_directory/*.deb") as $deb) {
$deb = str_replace($autobuild_builds_directory, "", $deb);
$path_components = explode("/", $deb);
$download_link = "download.php?jobid=".$path_components[1]."&pkg=".$path_components[2]."&file=".$path_components[3];
$package_debs[$path_components[3]] = $download_link;
}
}
return $package_debs;
}

23
web/global.php Normal file
View file

@ -0,0 +1,23 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
/*
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
*/
session_start();
require_once "global.db.php";
require_once "global.general.php";
require_once "global.job.php";
require_once "global.log.php";
require_once "global.config.php";
require_once "global.display.php";
require_once "global.cron.php";

BIN
web/img/cancel.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

252
web/img/config.svg Normal file
View file

@ -0,0 +1,252 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:ns1="http://sozi.baierouge.fr"
id="svg53383"
sodipodi:docname="applications-system.svg"
viewBox="0 0 48 48"
sodipodi:version="0.32"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
inkscape:version="0.46"
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/categories"
>
<defs
id="defs3"
>
<radialGradient
id="radialGradient2308"
fy="72.568"
cx="14.288"
gradientUnits="userSpaceOnUse"
cy="68.873"
r="11.69"
gradientTransform="matrix(1.3993 -2.2344e-7 8.1962e-8 .51326 4.3651 4.8393)"
inkscape:collect="always"
>
<stop
id="stop2302"
style="stop-color:#000000;stop-opacity:.32673"
offset="0"
/>
<stop
id="stop2304"
style="stop-color:#000000;stop-opacity:0"
offset="1"
/>
</radialGradient
>
<linearGradient
id="linearGradient3773"
y2="248.63"
gradientUnits="userSpaceOnUse"
x2="153"
gradientTransform="matrix(.20068 0 0 .20068 -54.336 -1.0508)"
y1="15.424"
x1="99.777"
inkscape:collect="always"
>
<stop
id="stop53300"
style="stop-color:#184375"
offset="0"
/>
<stop
id="stop53302"
style="stop-color:#C8BDDC"
offset="1"
/>
</linearGradient
>
</defs
>
<sodipodi:namedview
id="base"
bordercolor="#666666"
inkscape:pageshadow="2"
inkscape:window-y="151"
pagecolor="#ffffff"
inkscape:showpageshadow="false"
inkscape:grid-bbox="true"
inkscape:zoom="5.6568542"
inkscape:window-x="562"
inkscape:window-height="697"
showgrid="false"
borderopacity="0.11764706"
inkscape:current-layer="layer1"
inkscape:cx="43.652227"
inkscape:cy="21.164787"
inkscape:window-width="872"
inkscape:pageopacity="0.0"
inkscape:document-units="px"
/>
<g
id="layer2"
inkscape:label="shadow"
inkscape:groupmode="layer"
>
<path
id="path1538"
sodipodi:rx="19.928572"
sodipodi:ry="9.8372450"
style="fill-rule:evenodd;color:#000000;fill:url(#radialGradient2308)"
sodipodi:type="arc"
d="m44.286 38.714a19.929 9.8372 0 1 1 -39.857 0 19.929 9.8372 0 1 1 39.857 0z"
transform="matrix(1.1864 0 0 1.1864 -4.5397 -7.7947)"
sodipodi:cy="38.714287"
sodipodi:cx="24.357143"
/>
</g
>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
>
<path
id="path3243"
style="stroke-linejoin:round;color:#000000;stroke:#3f4561;stroke-linecap:round;fill:url(#linearGradient3773)"
inkscape:r_cy="true"
inkscape:r_cx="true"
d="m22.7 0.94747c-0.474 0.03238-0.934 0.10563-1.398 0.15883h-0.032l-1.112 6.068c-1.812 0.4127-3.517 1.1132-5.051 2.065l-4.988-3.59c-1.3485 1.0468-2.5754 2.2677-3.6536 3.59l3.4628 5.0517c-1.0514 1.606-1.842 3.441-2.2874 5.369v0.031l-6.0361 0.953c-0.1104 0.902-0.1589 1.833-0.1589 2.764 0 0.762 0.021 1.514 0.0953 2.256l6.0362 1.08c0.4293 2.096 1.2448 4.054 2.3827 5.782l-3.5899 4.924c1.0281 1.277 2.2151 2.439 3.4946 3.463l5.0833-3.494c1.776 1.133 3.759 1.928 5.909 2.319l0.953 6.004c0.677 0.062 1.372 0.064 2.065 0.064 0.979 0 1.914-0.037 2.859-0.159l1.144-6.132c2.041-0.507 3.958-1.389 5.623-2.573l4.893 3.558c1.268-1.079 2.429-2.32 3.431-3.653l-3.558-5.147c0.963-1.664 1.631-3.5 1.969-5.464l6.005-0.953c0.052-0.627 0.063-1.234 0.063-1.875 0-1.112-0.129-2.203-0.286-3.272l-6.099-1.112c-0.478-1.765-1.263-3.412-2.256-4.892l3.59-4.9245c-1.113-1.3608-2.382-2.618-3.781-3.6852l-5.178 3.5581c-1.488-0.8802-3.09-1.5556-4.829-1.9379l-0.953-6.0362c-0.868-0.102-1.742-0.15883-2.637-0.15883-0.242 0-0.491-0.00761-0.731 0-0.117 0.00371-0.232-0.00681-0.349 0-0.032 0.00184-0.064-0.00216-0.095 0zm0.826 15.44c0.116-0.006 0.231 0 0.349 0 3.761 0 6.83 3.07 6.83 6.831 0 3.76-3.069 6.798-6.83 6.798s-6.799-3.038-6.799-6.798c0-3.643 2.852-6.648 6.45-6.831z"
/>
<path
id="path3283"
sodipodi:rx="12.727922"
sodipodi:ry="12.727922"
style="opacity:.64773;color:#000000;stroke:#ffffff;stroke-width:1.6218;fill:none"
sodipodi:type="arc"
inkscape:r_cx="true"
transform="matrix(.61660 0 0 .61660 9.382 8.5397)"
sodipodi:cy="23.781593"
sodipodi:cx="23.511301"
inkscape:r_cy="true"
d="m36.239 23.782a12.728 12.728 0 1 1 -25.456 0 12.728 12.728 0 1 1 25.456 0z"
/>
<path
id="path3285"
style="opacity:.34659;color:#000000;stroke:#ffffff;fill:none"
inkscape:r_cy="true"
d="m21.996 2.1485l-0.893 5.875c-1.699 0.3869-4.824 1.5701-6.261 2.4625l-4.75-3.5454c-1.2639 0.9812-1.3505 1.0478-2.3611 2.2872l3.4341 5.0932c-0.985 1.506-2.1692 4.19-2.5942 6.108l-6.0178 1.014c-0.1035 0.845-0.0537 2.654 0.0159 3.349l5.7482 1.036c0.4024 1.965 1.9079 5.127 2.9749 6.747l-3.6351 4.803c0.9637 1.196 1.1566 1.306 2.3561 2.266l4.86-3.561c1.666 1.062 4.971 2.354 6.986 2.721l0.797 5.801c0.635 0.058 2.389 0.22 3.275 0.106l0.893-6.039c1.913-0.476 5.219-1.833 6.779-2.943l4.856 3.508c1.189-1.012 1.2-1.164 2.139-2.414l-3.598-5.114c0.903-1.56 2.071-4.611 2.388-6.452l5.891-0.977c0.049-0.588 0.052-2.225-0.095-3.228l-6.002-1.035c-0.448-1.655-1.986-4.636-2.917-6.024l3.815-4.8022c-1.043-1.2756-1.431-1.4506-2.742-2.451l-5.024 3.5982c-1.395-0.8252-4.177-2.083-5.807-2.4413l-0.887-5.7482c-0.814-0.0957-3.16-0.0532-3.624 0z"
sodipodi:nodetypes="ccccccccccccccccccccccccccccccccc"
inkscape:r_cx="true"
/>
<path
id="path3767"
style="opacity:.5;color:#000000;fill:#ffffff"
inkscape:r_cy="true"
inkscape:r_cx="true"
sodipodi:nodetypes="cccccccccsccccccccccccccccccccsccccc"
d="m10.103 6.2971c-1.3484 1.0468-1.9374 1.6748-3.0155 2.9971l3.4025 4.9648c-1.0515 1.599-2.1584 4.167-2.3476 5.729l-6.0625 1.044c-0.069 0.563-0.1737 1.853-0.1737 1.853l0.1768 1.562c0.4278 0.089 0.8402 0.171 1.2813 0.219l0.5-1.531c0.344 0.028 0.6796 0.062 1.0312 0.062 0.3512 0 0.7185-0.034 1.0625-0.062l0.4688 1.531c0.4413-0.048 0.8847-0.13 1.3125-0.219v-1.562c0.6857-0.16 1.332-0.388 1.9687-0.656l0.938 1.281c0.402-0.182 0.775-0.376 1.156-0.594l-0.5-1.5c0.597-0.362 1.162-0.795 1.687-1.25l1.282 0.938c0.325-0.297 0.641-0.612 0.937-0.938l-0.937-1.25c0.454-0.526 0.856-1.09 1.218-1.687l1.5 0.468c0.218-0.381 0.444-0.754 0.625-1.156l-1.281-0.937c0.269-0.637 0.465-1.283 0.625-1.969h1.594c0.088-0.428 0.139-0.871 0.187-1.313l-1.5-0.468c0.029-0.344 0.063-0.712 0.063-1.063s-0.034-0.687-0.063-1.0312l1.5-0.4687c-0.045-0.4199-0.105-0.842-0.187-1.25-1.148 0.3993-2.553 1.1531-3.569 1.7834l-4.88-3.5274z"
/>
<path
id="path3770"
style="opacity:.5;color:#000000;fill:#ffffff"
inkscape:r_cx="true"
inkscape:r_cy="true"
d="m37.237 17.218c-0.384 0.181-0.747 0.386-1.114 0.595l0.57 1.735c-0.697 0.422-1.355 0.92-1.968 1.45l-1.476-1.087c-0.38 0.346-0.742 0.707-1.087 1.087l1.087 1.476c-0.53 0.613-1.027 1.271-1.45 1.968l-0.544-0.181c-0.047 0.7-0.236 1.361-0.518 1.968l0.104 0.077c-0.313 0.743-0.565 1.531-0.751 2.331h-1.476c-0.136 0.085-0.27 0.185-0.414 0.259-0.078 0.415-0.135 0.817-0.181 1.242l1.76 0.57c-0.033 0.401-0.051 0.807-0.051 1.217s0.018 0.816 0.051 1.217l-1.76 0.57c0.056 0.514 0.13 1.028 0.233 1.527l1.838-0.026c0.128 0.55 0.303 1.108 0.492 1.632 0.745-0.288 1.474-0.63 2.149-1.036-0.411-1.216-0.647-2.529-0.647-3.884 0-5.198 3.292-9.637 7.897-11.341l-1.087-0.181c-0.223-0.823-0.512-1.629-0.881-2.383-0.01-0.022-0.014-0.055-0.025-0.077l-0.052-0.052-0.699-0.673z"
/>
</g
>
<metadata
>
<rdf:RDF
>
<cc:Work
>
<dc:format
>image/svg+xml</dc:format
>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
/>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
/>
<dc:publisher
>
<cc:Agent
rdf:about="http://openclipart.org/"
>
<dc:title
>Openclipart</dc:title
>
</cc:Agent
>
</dc:publisher
>
<dc:title
>tango applications system</dc:title
>
<dc:date
>2010-03-29T09:05:06</dc:date
>
<dc:description
>"System applications" icon from &lt;a href="http://tango.freedesktop.org/Tango_Desktop_Project"&gt; Tango Project &lt;/a&gt; &#13;\n&lt;br&gt;&lt;br&gt;&#13;\nSince version 0.8.90 Tango Project icons are Public Domain: &lt;a href="http://tango.freedesktop.org/Frequently_Asked_Questions#Terms_of_Use.3F"&gt; Tango Project FAQ &lt;/a&gt;</dc:description
>
<dc:source
>https://openclipart.org/detail/35413/tango-applications-system-by-warszawianka</dc:source
>
<dc:creator
>
<cc:Agent
>
<dc:title
>warszawianka</dc:title
>
</cc:Agent
>
</dc:creator
>
<dc:subject
>
<rdf:Bag
>
<rdf:li
>externalsource</rdf:li
>
<rdf:li
>gear</rdf:li
>
<rdf:li
>icon</rdf:li
>
<rdf:li
>metal</rdf:li
>
<rdf:li
>tango</rdf:li
>
</rdf:Bag
>
</dc:subject
>
</cc:Work
>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"
>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
/>
</cc:License
>
</rdf:RDF
>
</metadata
>
</svg
>

After

Width:  |  Height:  |  Size: 10 KiB

BIN
web/img/refresh.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

279
web/img/vm.svg Normal file
View file

@ -0,0 +1,279 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:ns1="http://sozi.baierouge.fr"
id="svg2"
sodipodi:modified="TRUE"
viewBox="0 0 30 30"
sodipodi:docbase="D:\gfx"
inkscape:export-ydpi="45"
inkscape:export-xdpi="45"
version="1.0"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
sodipodi:docname="icon_console.svg"
inkscape:export-filename="D:\proj\cliparts\released\icon_console.png"
inkscape:version="0.46"
sodipodi:version="0.32"
>
<defs
id="defs4"
>
<linearGradient
id="linearGradient2422"
y2="25"
gradientUnits="userSpaceOnUse"
x2="28"
y1="2"
x1="2"
inkscape:collect="always"
>
<stop
id="stop3214"
style="stop-color:#eeeeec"
offset="0"
/>
<stop
id="stop3217"
style="stop-color:#8a8a7c"
offset="1"
/>
</linearGradient
>
<linearGradient
id="linearGradient2425"
y2="27"
gradientUnits="userSpaceOnUse"
x2="30"
gradientTransform="matrix(.93333 0 0 .92593 1 1)"
inkscape:collect="always"
>
<stop
id="stop3206"
style="stop-color:#b0b1ae"
offset="0"
/>
<stop
id="stop3208"
style="stop-color:#5f5f5d"
offset="1"
/>
</linearGradient
>
</defs
>
<sodipodi:namedview
id="base"
inkscape:zoom="22.966667"
height="30px"
borderopacity="1.0"
inkscape:current-layer="layer1"
inkscape:cx="21.596517"
inkscape:cy="15"
inkscape:grid-points="true"
showgrid="false"
width="30px"
inkscape:snap-global="true"
showguides="true"
bordercolor="#666666"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-width="1440"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
pagecolor="#ffffff"
gridtolerance="10000"
inkscape:document-units="px"
inkscape:window-height="885"
>
<inkscape:grid
id="grid2395"
type="xygrid"
/>
</sodipodi:namedview
>
<g
id="layer1"
inkscape:label="Calque 1"
inkscape:groupmode="layer"
>
<rect
id="rect3465"
style="fill:#427bc3"
ry="0"
height="0"
width="6"
y="12.5"
x="16"
/>
<g
id="g2427"
>
<path
id="rect2420"
style="fill:url(#linearGradient2425)"
d="m4.9839 1h20.032c2.207 0 3.984 1.7768 3.984 3.9839v17.032c0 2.207-1.777 3.984-3.984 3.984h-20.032c-2.2072 0-3.984-1.777-3.984-3.984v-17.032c0-2.2072 1.7768-3.984 3.9839-3.984z"
/>
<path
id="rect2422"
style="fill:url(#linearGradient2422)"
d="m4.9441 2h20.112c1.631 0 2.944 1.3131 2.944 2.9441v17.112c0 1.631-1.313 2.944-2.944 2.944h-20.112c-1.6309 0-2.944-1.313-2.944-2.944v-17.112c0-1.6309 1.3131-2.944 2.9441-2.944z"
/>
<path
id="rect2424"
style="fill:#3465a4"
d="m5 5h20v17h-20v-17z"
/>
<path
id="rect3231"
style="fill:#555753"
d="m13 26h4v2h-4v-2z"
/>
<path
id="rect3233"
style="fill:#888a85"
d="m5.5 28h19c0.277 0 0.5 0.223 0.5 0.5s-0.223 0.5-0.5 0.5h-19c-0.277 0-0.5-0.223-0.5-0.5s0.223-0.5 0.5-0.5z"
/>
<path
id="rect3245"
style="fill:#555753"
d="m4 4v19h22v-19h-22zm1 1h20v17h-20v-17z"
/>
<g
id="g2431"
transform="matrix(.86806 0 0 .86806 2.0708 2.0711)"
>
<path
id="path2419"
style="fill:#888a85"
d="m6.5312 7.125c-0.2398 0.0443-0.412 0.2562-0.4062 0.5v2.125c-0.0031 0.2059 0.1212 0.392 0.3125 0.469l6.0005 2.343-6.0005 2.344c-0.1913 0.077-0.3156 0.263-0.3125 0.469v2.125c-0.0036 0.168 0.078 0.326 0.2167 0.421 0.1387 0.094 0.3158 0.112 0.4708 0.048l9.5625-3.938c0.191-0.076 0.316-0.263 0.313-0.469v-2c0.003-0.205-0.122-0.392-0.313-0.468l-9.5625-3.9378c-0.0887-0.0374-0.1865-0.0483-0.2813-0.0312z"
/>
<path
id="path3281"
style="fill:#eeeeec"
d="m6.6218 9.7397v-2.1279l9.5802 3.9412v2.008l-9.5802 3.941v-2.128l7.2112-2.808-7.2112-2.8263z"
/>
</g
>
<g
id="g2438"
transform="matrix(.87731 0 0 .87731 2.5457 2.2604)"
>
<path
id="path2424"
style="fill:#888a85"
d="m16.094 17.719c-0.19 0.06-0.317 0.238-0.313 0.437v1.25c0.006 0.239 0.199 0.432 0.438 0.438h6.656c0.239-0.006 0.432-0.199 0.437-0.438v-1.25c-0.005-0.239-0.198-0.432-0.437-0.437h-6.656c-0.042-0.006-0.084-0.006-0.125 0z"
/>
<path
id="text3274"
style="fill:#eeeeec"
d="m22.881 18.157v1.236h-6.665v-1.236h6.665z"
/>
</g
>
</g
>
</g
>
<metadata
>
<rdf:RDF
>
<cc:Work
>
<dc:format
>image/svg+xml</dc:format
>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
/>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
/>
<dc:publisher
>
<cc:Agent
rdf:about="http://openclipart.org/"
>
<dc:title
>Openclipart</dc:title
>
</cc:Agent
>
</dc:publisher
>
<dc:title
>icon_console</dc:title
>
<dc:date
>2009-03-20T19:29:01</dc:date
>
<dc:description
>Icon in 16x16 px</dc:description
>
<dc:source
>https://openclipart.org/detail/22674/icon_console-by-jean_victor_balin</dc:source
>
<dc:creator
>
<cc:Agent
>
<dc:title
>jean_victor_balin</dc:title
>
</cc:Agent
>
</dc:creator
>
<dc:subject
>
<rdf:Bag
>
<rdf:li
>computer</rdf:li
>
<rdf:li
>console</rdf:li
>
<rdf:li
>icon</rdf:li
>
<rdf:li
>system</rdf:li
>
<rdf:li
>terminal</rdf:li
>
</rdf:Bag
>
</dc:subject
>
</cc:Work
>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"
>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
/>
</cc:License
>
</rdf:RDF
>
</metadata
>
</svg
>

After

Width:  |  Height:  |  Size: 7.4 KiB

687
web/img/web.svg Normal file
View file

@ -0,0 +1,687 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:ns1="http://sozi.baierouge.fr"
id="svg3440"
sodipodi:docname="internet-web-browser.svg"
viewBox="0 0 48 48"
sodipodi:version="0.32"
inkscape:output_extension="org.inkscape.output.svg.inkscape"
inkscape:version="0.46"
sodipodi:docbase="/home/jimmac/src/cvs/tango-icon-theme/scalable/apps"
>
<defs
id="defs3"
>
<linearGradient
id="linearGradient4750"
>
<stop
id="stop4752"
style="stop-color:#ffffff"
offset="0"
/>
<stop
id="stop4758"
style="stop-color:#fefefe"
offset=".37931"
/>
<stop
id="stop4754"
style="stop-color:#1d1d1d"
offset="1"
/>
</linearGradient
>
<radialGradient
id="radialGradient3968"
gradientUnits="userSpaceOnUse"
cy="15.716"
cx="18.248"
gradientTransform="scale(.99999 1)"
r="29.993"
inkscape:collect="always"
>
<stop
id="stop3964"
style="stop-color:#d3e9ff"
offset="0"
/>
<stop
id="stop4134"
style="stop-color:#d3e9ff"
offset=".15517"
/>
<stop
id="stop4346"
style="stop-color:#4074ae"
offset=".75"
/>
<stop
id="stop3966"
style="stop-color:#36486c"
offset="1"
/>
</radialGradient
>
<radialGradient
id="radialGradient4120"
gradientUnits="userSpaceOnUse"
cy="63.965"
cx="15.116"
gradientTransform="scale(1.644 .60828)"
r="12.289"
inkscape:collect="always"
>
<stop
id="stop4116"
style="stop-color:#000000"
offset="0"
/>
<stop
id="stop4118"
style="stop-color:#000000;stop-opacity:0"
offset="1"
/>
</radialGradient
>
<radialGradient
id="radialGradient4132"
gradientUnits="userSpaceOnUse"
cy="12.142"
cx="15.601"
gradientTransform="scale(.99999 1)"
r="43.527"
inkscape:collect="always"
>
<stop
id="stop4128"
style="stop-color:#ffffff"
offset="0"
/>
<stop
id="stop4130"
style="stop-color:#ffffff;stop-opacity:.16495"
offset="1"
/>
</radialGradient
>
<radialGradient
id="radialGradient4356"
gradientUnits="userSpaceOnUse"
cy="10.476"
cx="11.827"
gradientTransform="scale(1.1795 .84779)"
r="32.665"
inkscape:collect="always"
>
<stop
id="stop4352"
style="stop-color:#ffffff"
offset="0"
/>
<stop
id="stop4354"
style="stop-color:#ffffff;stop-opacity:0"
offset="1"
/>
</radialGradient
>
<radialGradient
id="radialGradient4756"
fx="18.934"
fy="17.81"
xlink:href="#linearGradient4750"
gradientUnits="userSpaceOnUse"
cy="17.486"
cx="18.634"
gradientTransform="scale(1.0368 .96449)"
r="40.693"
inkscape:collect="always"
/>
</defs
>
<sodipodi:namedview
id="base"
bordercolor="#666666"
inkscape:pageshadow="2"
inkscape:window-y="30"
pagecolor="#ffffff"
inkscape:window-height="823"
inkscape:grid-bbox="true"
inkscape:zoom="9.8994949"
inkscape:window-x="0"
showgrid="false"
borderopacity="0.17254902"
inkscape:current-layer="layer1"
inkscape:cx="25.799661"
inkscape:cy="24.622653"
inkscape:showpageshadow="false"
inkscape:window-width="1440"
inkscape:pageopacity="0.0"
inkscape:document-units="px"
/>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer"
>
<path
id="path4112"
sodipodi:rx="20.203051"
sodipodi:ry="7.4751287"
style="fill:url(#radialGradient4120)"
sodipodi:type="arc"
d="m45.053 38.909a20.203 7.4751 0 1 1 -40.406 0 20.203 7.4751 0 1 1 40.406 0z"
transform="matrix(1 0 0 1.2432 0 -10.272)"
sodipodi:cy="38.908627"
sodipodi:cx="24.849752"
/>
<path
id="path3214"
style="stroke:#39396c;fill:url(#radialGradient3968)"
d="m43.96 23.485c0 10.71-8.682 19.392-19.39 19.392-10.71 0-19.391-8.682-19.391-19.392-0.0003-10.709 8.681-19.39 19.391-19.39 10.708 0.0002 19.39 8.681 19.39 19.39z"
/>
<path
id="path4348"
sodipodi:rx="12.929953"
sodipodi:ry="9.2934036"
style="opacity:.42159;fill:url(#radialGradient4356)"
sodipodi:type="arc"
d="m30.709 15.271a12.93 9.2934 0 1 1 -25.86 0 12.93 9.2934 0 1 1 25.86 0z"
transform="matrix(.83594 0 0 1 9.8869 0)"
sodipodi:cy="15.271057"
sodipodi:cx="17.778685"
/>
<g
id="g4136"
style="fill:#000000;fill-opacity:.71345"
transform="matrix(.98237 0 0 .98237 .12108 .23291)"
>
<g
id="g4138"
>
<g
id="g4142"
>
<path
id="path4144"
d="m44.071 20.714v0l-0.545 0.618c-0.334-0.394-0.709-0.725-1.089-1.071l-0.836 0.123-0.764-0.863v1.068l0.654 0.495 0.436 0.494 0.582-0.658c0.146 0.274 0.291 0.548 0.436 0.823v0.822l-0.655 0.74-1.199 0.823-0.908 0.907-0.582-0.661 0.291-0.74-0.582-0.658-0.981-2.098-0.836-0.945-0.219 0.246 0.328 1.194 0.618 0.699c0.352 1.017 0.701 1.99 1.164 2.963 0.718 0 1.394-0.077 2.107-0.166v0.576l-0.872 2.139-0.8 0.904-0.654 1.401v2.303l0.219 0.906-0.364 0.41-0.8 0.494-0.836 0.699 0.691 0.782-0.945 0.824 0.182 0.533-1.418 1.606h-0.945l-0.8 0.494h-0.509v-0.659l-0.217-1.318c-0.281-0.826-0.574-1.647-0.872-2.467 0-0.605 0.036-1.205 0.072-1.81l0.364-0.823-0.509-0.988 0.037-1.357-0.692-0.782 0.346-1.13-0.563-0.639h-0.982l-0.327-0.37-0.981 0.618-0.4-0.454-0.909 0.782c-0.617-0.7-1.235-1.399-1.854-2.098l-0.726-1.729 0.654-0.986-0.363-0.411 0.799-1.894c0.656-0.816 1.341-1.599 2.035-2.385l1.236-0.329 1.381-0.165 0.945 0.248 1.345 1.356 0.473-0.534 0.653-0.082 1.236 0.411h0.946l0.654-0.576 0.291-0.411-0.655-0.412-1.091-0.082c-0.303-0.419-0.584-0.861-0.944-1.234l-0.364 0.164-0.145 1.07-0.655-0.74-0.144-0.824-0.727-0.574h-0.292l0.728 0.822-0.291 0.74-0.581 0.164 0.363-0.74-0.655-0.328-0.581-0.658-1.091 0.246-0.145 0.328-0.654 0.412-0.363 0.906-0.909 0.452-0.4-0.452h-0.436v-1.482l0.946-0.494h0.726l-0.146-0.575-0.58-0.576 0.98-0.206 0.545-0.617 0.436-0.741h0.801l-0.219-0.575 0.509-0.329v0.658l1.09 0.246 1.09-0.904 0.073-0.412 0.945-0.658c-0.342 0.043-0.684 0.074-1.018 0.165v-0.7414l0.363-0.8228h-0.363l-0.798 0.7402-0.219 0.412 0.219 0.576-0.365 0.987-0.581-0.329-0.508-0.576-0.8 0.576-0.291-1.316 1.381-0.9052v-0.4941l0.872-0.5757 1.381-0.3296 0.946 0.3296 1.744 0.3291-0.436 0.4932h-0.945l0.945 0.9877 0.727-0.8227 0.221-0.3618s2.787 2.4975 4.379 5.2305c1.593 2.733 2.341 5.955 2.341 6.609z"
/>
</g
>
</g
>
<g
id="g4146"
>
<g
id="g4150"
>
<path
id="path4152"
d="m26.07 9.2363l-0.073 0.4932 0.51 0.3295 0.871-0.5761-0.436-0.4937-0.582 0.3296-0.29-0.0825"
/>
</g
>
</g
>
<g
id="g4154"
>
<g
id="g4158"
>
<path
id="path4160"
d="m26.87 5.8633l-1.89-0.7407-2.18 0.2466-2.691 0.7402-0.508 0.4941 1.671 1.1514v0.6582l-0.654 0.6582 0.873 1.7287 0.58-0.33 0.729-1.1512c1.123-0.3472 2.13-0.7407 3.197-1.2344l0.873-2.2212"
/>
</g
>
</g
>
<g
id="g4162"
>
<g
id="g4166"
>
<path
id="path4168"
d="m28.833 12.775l-0.291-0.741-0.51 0.165 0.147 0.904 0.654-0.328"
/>
</g
>
</g
>
<g
id="g4170"
>
<g
id="g4174"
>
<path
id="path4176"
d="m29.123 12.609l-0.145 0.988 0.799-0.165 0.581-0.575-0.508-0.494c-0.171-0.455-0.368-0.88-0.582-1.317h-0.435v0.494l0.29 0.329v0.74"
/>
</g
>
</g
>
<g
id="g4178"
>
<g
id="g4182"
>
<path
id="path4184"
d="m18.365 28.242l-0.582-1.152-1.09-0.247-0.582-1.562-1.453 0.164-1.236-0.904-1.309 1.151v0.182c-0.396-0.115-0.883-0.13-1.235-0.347l-0.291-0.822v-0.906l-0.8722 0.082c0.0728-0.576 0.145-1.151 0.2183-1.727h-0.5093l-0.5083 0.658-0.5093 0.246-0.7271-0.41-0.0728-0.905 0.1455-0.988 1.0908-0.822h0.8721l0.145-0.494 1.0903 0.246 0.8 0.988 0.145-1.646 1.382-1.152 0.508-1.234 1.018-0.411 0.581-0.822 1.309-0.248 0.654-0.987h-1.963l1.236-0.576h0.872l1.236-0.412 0.146-0.492-0.437-0.412-0.509-0.165 0.146-0.494-0.363-0.74-0.873 0.328 0.146-0.657-1.018-0.5765-0.799 1.3975 0.072 0.494-0.799 0.331-0.51 1.069-0.218-0.987-1.381-0.577-0.218-0.74 1.817-1.0701 0.8-0.7402 0.073-0.9048-0.436-0.2471-0.582-0.0825-0.363 0.9053s-0.608 0.1191-0.764 0.1577c-1.996 1.8397-6.0294 5.8097-6.9664 13.306 0.0371 0.174 0.6792 1.182 0.6792 1.182l1.5264 0.904 1.5264 0.412 0.6544 0.824 1.018 0.74 0.581-0.082 0.436 0.196v0.133l-0.581 1.563-0.437 0.658 0.146 0.33-0.363 1.233 1.308 2.386 1.308 1.153 0.582 0.822-0.073 1.728 0.437 0.987-0.437 1.892s-0.034-0.011 0.022 0.178c0.056 0.19 2.329 1.451 2.473 1.344 0.144-0.109 0.267-0.205 0.267-0.205l-0.145-0.41 0.581-0.577 0.219-0.576 0.945-0.33 0.727-1.81-0.218-0.493 0.508-0.74 1.09-0.248 0.582-1.316-0.145-1.645 0.872-1.234 0.145-1.235c-1.193-0.591-2.376-1.201-3.561-1.81"
/>
</g
>
</g
>
<g
id="g4186"
>
<g
id="g4190"
>
<path
id="path4192"
d="m16.766 9.5649l0.726 0.4941h0.582v-0.5761l-0.726-0.3291-0.582 0.4111"
/>
</g
>
</g
>
<g
id="g4194"
>
<g
id="g4198"
>
<path
id="path4200"
d="m14.876 8.9072l-0.364 0.9048h0.727l0.364-0.8228c0.314-0.2217 0.626-0.4448 0.945-0.6582l0.727 0.2471c0.484 0.3291 0.969 0.6582 1.454 0.9868l0.727-0.6577-0.8-0.3291-0.364-0.7407-1.381-0.1646-0.073-0.4116-0.654 0.165-0.29 0.5758-0.364-0.7407-0.145 0.3291 0.073 0.8228-0.582 0.494"
/>
</g
>
</g
>
<g
id="g4202"
>
<g
id="g4204"
style="opacity:.75"
/>
<g
id="g4208"
/>
</g
>
<g
id="g4212"
>
<g
id="g4214"
style="opacity:.75"
/>
<g
id="g4218"
/>
</g
>
<g
id="g4222"
>
<g
id="g4226"
>
<path
id="path4228"
d="m17.492 6.8496l0.364-0.3286 0.727-0.1646c0.498-0.2422 0.998-0.4053 1.527-0.5762l-0.29-0.4937-0.939 0.1348-0.443 0.4419-0.731 0.106-0.65 0.3052-0.316 0.1528-0.193 0.2583 0.944 0.1641"
/>
</g
>
</g
>
<g
id="g4230"
>
<g
id="g4234"
>
<path
id="path4236"
d="m18.728 14.666l0.437-0.658-0.655-0.493 0.218 1.151"
/>
</g
>
</g
>
</g
>
<g
id="g3216"
style="color:#000000;fill:url(#radialGradient4756)"
transform="matrix(.98237 0 0 .98237 -.080952 .030883)"
>
<g
id="g3218"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3222"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3224"
style="color:#000000;fill:url(#radialGradient4756)"
d="m44.071 20.714v0l-0.545 0.618c-0.334-0.394-0.709-0.725-1.089-1.071l-0.836 0.123-0.764-0.863v1.068l0.654 0.495 0.436 0.494 0.582-0.658c0.146 0.274 0.291 0.548 0.436 0.823v0.822l-0.655 0.74-1.199 0.823-0.908 0.907-0.582-0.661 0.291-0.74-0.582-0.658-0.981-2.098-0.836-0.945-0.219 0.246 0.328 1.194 0.618 0.699c0.352 1.017 0.701 1.99 1.164 2.963 0.718 0 1.394-0.077 2.107-0.166v0.576l-0.872 2.139-0.8 0.904-0.654 1.401v2.303l0.219 0.906-0.364 0.41-0.8 0.494-0.836 0.699 0.691 0.782-0.945 0.824 0.182 0.533-1.418 1.606h-0.945l-0.8 0.494h-0.509v-0.659l-0.217-1.318c-0.281-0.826-0.574-1.647-0.872-2.467 0-0.605 0.036-1.205 0.072-1.81l0.364-0.823-0.509-0.988 0.037-1.357-0.692-0.782 0.346-1.13-0.563-0.639h-0.982l-0.327-0.37-0.981 0.618-0.4-0.454-0.909 0.782c-0.617-0.7-1.235-1.399-1.854-2.098l-0.726-1.729 0.654-0.986-0.363-0.411 0.799-1.894c0.656-0.816 1.341-1.599 2.035-2.385l1.236-0.329 1.381-0.165 0.945 0.248 1.345 1.356 0.473-0.534 0.653-0.082 1.236 0.411h0.946l0.654-0.576 0.291-0.411-0.655-0.412-1.091-0.082c-0.303-0.419-0.584-0.861-0.944-1.234l-0.364 0.164-0.145 1.07-0.655-0.74-0.144-0.824-0.727-0.574h-0.292l0.728 0.822-0.291 0.74-0.581 0.164 0.363-0.74-0.655-0.328-0.581-0.658-1.091 0.246-0.145 0.328-0.654 0.412-0.363 0.906-0.909 0.452-0.4-0.452h-0.436v-1.482l0.946-0.494h0.726l-0.146-0.575-0.58-0.576 0.98-0.206 0.545-0.617 0.436-0.741h0.801l-0.219-0.575 0.509-0.329v0.658l1.09 0.246 1.09-0.904 0.073-0.412 0.945-0.658c-0.342 0.043-0.684 0.074-1.018 0.165v-0.7414l0.363-0.8228h-0.363l-0.798 0.7402-0.219 0.412 0.219 0.576-0.365 0.987-0.581-0.329-0.508-0.576-0.8 0.576-0.291-1.316 1.381-0.9052v-0.4941l0.872-0.5757 1.381-0.3296 0.946 0.3296 1.744 0.3291-0.436 0.4932h-0.945l0.945 0.9877 0.727-0.8227 0.221-0.3618s2.787 2.4975 4.379 5.2305c1.593 2.733 2.341 5.955 2.341 6.609z"
/>
</g
>
</g
>
<g
id="g3226"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3230"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3232"
style="color:#000000;fill:url(#radialGradient4756)"
d="m26.07 9.2363l-0.073 0.4932 0.51 0.3295 0.871-0.5761-0.436-0.4937-0.582 0.3296-0.29-0.0825"
/>
</g
>
</g
>
<g
id="g3234"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3238"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3240"
style="color:#000000;fill:url(#radialGradient4756)"
d="m26.87 5.8633l-1.89-0.7407-2.18 0.2466-2.691 0.7402-0.508 0.4941 1.671 1.1514v0.6582l-0.654 0.6582 0.873 1.7287 0.58-0.33 0.729-1.1512c1.123-0.3472 2.13-0.7407 3.197-1.2344l0.873-2.2212"
/>
</g
>
</g
>
<g
id="g3242"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3246"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3248"
style="color:#000000;fill:url(#radialGradient4756)"
d="m28.833 12.775l-0.291-0.741-0.51 0.165 0.147 0.904 0.654-0.328"
/>
</g
>
</g
>
<g
id="g3250"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3254"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3256"
style="color:#000000;fill:url(#radialGradient4756)"
d="m29.123 12.609l-0.145 0.988 0.799-0.165 0.581-0.575-0.508-0.494c-0.171-0.455-0.368-0.88-0.582-1.317h-0.435v0.494l0.29 0.329v0.74"
/>
</g
>
</g
>
<g
id="g3258"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3262"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3264"
style="color:#000000;fill:url(#radialGradient4756)"
d="m18.365 28.242l-0.582-1.152-1.09-0.247-0.582-1.562-1.453 0.164-1.236-0.904-1.309 1.151v0.182c-0.396-0.115-0.883-0.13-1.235-0.347l-0.291-0.822v-0.906l-0.8722 0.082c0.0728-0.576 0.145-1.151 0.2183-1.727h-0.5093l-0.5083 0.658-0.5093 0.246-0.7271-0.41-0.0728-0.905 0.1455-0.988 1.0908-0.822h0.8721l0.145-0.494 1.0903 0.246 0.8 0.988 0.145-1.646 1.382-1.152 0.508-1.234 1.018-0.411 0.581-0.822 1.309-0.248 0.654-0.987h-1.963l1.236-0.576h0.872l1.236-0.412 0.146-0.492-0.437-0.412-0.509-0.165 0.146-0.494-0.363-0.74-0.873 0.328 0.146-0.657-1.018-0.5765-0.799 1.3975 0.072 0.494-0.799 0.331-0.51 1.069-0.218-0.987-1.381-0.577-0.218-0.74 1.817-1.0701 0.8-0.7402 0.073-0.9048-0.436-0.2471-0.582-0.0825-0.363 0.9053s-0.608 0.1191-0.764 0.1577c-1.996 1.8397-6.0294 5.8097-6.9664 13.306 0.0371 0.174 0.6792 1.182 0.6792 1.182l1.5264 0.904 1.5264 0.412 0.6544 0.824 1.018 0.74 0.581-0.082 0.436 0.196v0.133l-0.581 1.563-0.437 0.658 0.146 0.33-0.363 1.233 1.308 2.386 1.308 1.153 0.582 0.822-0.073 1.728 0.437 0.987-0.437 1.892s-0.034-0.011 0.022 0.178c0.056 0.19 2.329 1.451 2.473 1.344 0.144-0.109 0.267-0.205 0.267-0.205l-0.145-0.41 0.581-0.577 0.219-0.576 0.945-0.33 0.727-1.81-0.218-0.493 0.508-0.74 1.09-0.248 0.582-1.316-0.145-1.645 0.872-1.234 0.145-1.235c-1.193-0.591-2.376-1.201-3.561-1.81"
/>
</g
>
</g
>
<g
id="g3266"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3270"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3272"
style="color:#000000;fill:url(#radialGradient4756)"
d="m16.766 9.5649l0.726 0.4941h0.582v-0.5761l-0.726-0.3291-0.582 0.4111"
/>
</g
>
</g
>
<g
id="g3274"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3278"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3280"
style="color:#000000;fill:url(#radialGradient4756)"
d="m14.876 8.9072l-0.364 0.9048h0.727l0.364-0.8228c0.314-0.2217 0.626-0.4448 0.945-0.6582l0.727 0.2471c0.484 0.3291 0.969 0.6582 1.454 0.9868l0.727-0.6577-0.8-0.3291-0.364-0.7407-1.381-0.1646-0.073-0.4116-0.654 0.165-0.29 0.5758-0.364-0.7407-0.145 0.3291 0.073 0.8228-0.582 0.494"
/>
</g
>
</g
>
<g
id="g3282"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3284"
style="opacity:.75;color:#000000;fill:url(#radialGradient4756)"
/>
<g
id="g3288"
style="color:#000000;fill:url(#radialGradient4756)"
/>
</g
>
<g
id="g3292"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3294"
style="opacity:.75;color:#000000;fill:url(#radialGradient4756)"
/>
<g
id="g3298"
style="color:#000000;fill:url(#radialGradient4756)"
/>
</g
>
<g
id="g3302"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3306"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3308"
style="color:#000000;fill:url(#radialGradient4756)"
d="m17.492 6.8496l0.364-0.3286 0.727-0.1646c0.498-0.2422 0.998-0.4053 1.527-0.5762l-0.29-0.4937-0.939 0.1348-0.443 0.4419-0.731 0.106-0.65 0.3052-0.316 0.1528-0.193 0.2583 0.944 0.1641"
/>
</g
>
</g
>
<g
id="g3310"
style="color:#000000;fill:url(#radialGradient4756)"
>
<g
id="g3314"
style="color:#000000;fill:url(#radialGradient4756)"
>
<path
id="path3316"
style="color:#000000;fill:url(#radialGradient4756)"
d="m18.728 14.666l0.437-0.658-0.655-0.493 0.218 1.151"
/>
</g
>
</g
>
</g
>
<path
id="path4122"
style="stroke:url(#radialGradient4132);fill:none"
d="m42.975 23.486c0 10.165-8.241 18.406-18.406 18.406s-18.406-8.241-18.406-18.406c0.0004-10.166 8.241-18.406 18.406-18.406 10.165-0.0001 18.406 8.24 18.406 18.406z"
/>
</g
>
<metadata
>
<rdf:RDF
>
<cc:Work
>
<dc:format
>image/svg+xml</dc:format
>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage"
/>
<cc:license
rdf:resource="http://creativecommons.org/licenses/publicdomain/"
/>
<dc:publisher
>
<cc:Agent
rdf:about="http://openclipart.org/"
>
<dc:title
>Openclipart</dc:title
>
</cc:Agent
>
</dc:publisher
>
<dc:title
>tango inetrnet web browser</dc:title
>
<dc:date
>2010-03-29T08:08:30</dc:date
>
<dc:description
>"Web browser" icon from &lt;a href="http://tango.freedesktop.org/Tango_Desktop_Project"&gt; Tango Project &lt;/a&gt; &#13;\n&lt;br&gt;&lt;br&gt;&#13;\nSince version 0.8.90 Tango Project icons are Public Domain: &lt;a href="http://tango.freedesktop.org/Frequently_Asked_Questions#Terms_of_Use.3F"&gt; Tango Project FAQ &lt;/a&gt;</dc:description
>
<dc:source
>https://openclipart.org/detail/35233/tango-inetrnet-web-browser-by-warszawianka</dc:source
>
<dc:creator
>
<cc:Agent
>
<dc:title
>warszawianka</dc:title
>
</cc:Agent
>
</dc:creator
>
<dc:subject
>
<rdf:Bag
>
<rdf:li
>blue</rdf:li
>
<rdf:li
>earth</rdf:li
>
<rdf:li
>externalsource</rdf:li
>
<rdf:li
>glopbe</rdf:li
>
<rdf:li
>icon</rdf:li
>
<rdf:li
>planet</rdf:li
>
<rdf:li
>tango</rdf:li
>
</rdf:Bag
>
</dc:subject
>
</cc:Work
>
<cc:License
rdf:about="http://creativecommons.org/licenses/publicdomain/"
>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution"
/>
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
/>
</cc:License
>
</rdf:RDF
>
</metadata
>
</svg
>

After

Width:  |  Height:  |  Size: 23 KiB

34
web/index.php Normal file
View file

@ -0,0 +1,34 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
display_header();
display_error_message();
?>
<main>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<div class="card" id="dashboard">
<h2>Dashboard</h2>
<p>Welcome to Autobuild, your automated Debian package builder and distributor.</p>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

116
web/log.php Normal file
View file

@ -0,0 +1,116 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
$log_number = $_GET["log"];
if (!isset($_GET["error"])) {
$log_file = get_log_file($log_number);
while (filesize($log_file) === 0) {
sleep(1); // Sleep until we can get the job info
clearstatcache(); // Don't cache the result of filesize()
}
// Get the PID and the Job ID
$autobuild_pid = get_job_pid($log_number);
$autobuild_status = get_job_status($log_number);
// Are we canceling the job?
// If the user asked to cancel, first make sure that the job is actually running (first bit in the status code = 1)
if (isset($_GET["cancel"]) && $autobuild_status & 4) {
run_autobuild("-k $autobuild_pid");
}
// Get the individual package build logs
$package_logs = get_package_build_log_names($log_number);
$tab = "main";
if (isset($_GET["tab"]) && in_array($_GET["tab"], $package_logs)) {
$tab = $_GET["tab"];
}
}
display_header();
display_error_message();
?>
<main>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<div class="card" id="new-build">
<div style="width: 100%; overflow: hidden;">
<div style="width: 50%; float: left;">
<h2>Build Log</h2>
</div>
<?php
if ($autobuild_status & 4) {
echo '
<div id="control-buttons" style="margin-left: calc(100% - 85px);">
<a href="' . basename($_SERVER["PHP_SELF"]) . "?log=" . $_GET["log"] . '&cancel=true">
<img src="img/cancel.webp" width="30px" height="30px" title="Cancel build" />
</a>
&nbsp; &nbsp;
<a href="' . basename($_SERVER["PHP_SELF"]) . "?log=" . $_GET["log"] . '&tab='. $tab .'">
<img src="img/refresh.webp" width="30px" height="30px" title="Refresh log" />
</a>
</div>';
}
?>
</div>
<label for="build-log">Build:
<?php
echo print_status_code($autobuild_status, true);
?>
</label>
<br>
<div class="tabs" id="tabs">
<?php
echo '<a href="' . basename($_SERVER["PHP_SELF"]) . "?log=" . $_GET["log"] . '&tab=main#tabs" class="tab' . ($tab == "main" ? " active" : "") . '">Main</a>';
foreach ($package_logs as $package_log) {
echo '<a href="' . basename($_SERVER["PHP_SELF"]) . "?log=" . $_GET["log"] . '&tab=' . $package_log . '#tabs" class="tab' . ($tab == $package_log ? " active" : "") . '">' . $package_log . '</a>';
}
?>
</div>
<div id="log">
<?php
if (!isset($_GET["error"])) {
echo ' <iframe src="view-log.php?log='.$_GET["log"].'&tab='.$tab.'#end" title="Build log" height="400" width="100%" id="build-log-iframe"></iframe><br>';
}
?>
</div>
</div>
<div class="card" id="new-build">
<div style="width: 100%; overflow: hidden;">
<h2>Files</h2>
<?php
if ($autobuild_status & 2) {
foreach (get_download_links($log_number) as $file => $link) {
echo "<a href=\"$link\">$file</a><br>";
}
}
?>
</div>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

69
web/login.php Normal file
View file

@ -0,0 +1,69 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
if ($_SESSION['logged-in']) {
redirect_and_die("index.php");
}
if (isset($_POST["submitted"])) {
// Form submitted, create a new user account
$username = $_POST["username"];
$password = $_POST["password"];
$captcha = $_POST["captcha"];
if (strtolower($captcha) != strtolower($_SESSION["captcha"]) || empty($captcha)) {
$params["error"] = "invalid-captcha";
redirect_and_die("login.php", $params);
}
if ($username == "" || $password == "") {
$params["error"] = "form-incomplete";
redirect_and_die("login.php", $params);
}
if (!log_in($username, $password)) {
$params["error"] = "invalid-credentials";
redirect_and_die("login.php", $params);
}
$_SESSION['logged-in'] = true;
redirect_and_die("index.php");
}
display_header();
display_error_message();
?>
<main>
<div class="container">
<div class="content-wrapper">
<section class="main-content">
<div class="card" id="login">
<h2>Log In</h2>
<form action="login.php" method="post">
<input type="text" name="username" placeholder="Username">
<input type="password" name="password" placeholder="Password">
<br>
<li>
<input type="text" name="captcha" class="captcha" placeholder="Captcha">
<iframe src="view-captcha.php" class="captcha"></iframe>
</li>
<input type="hidden" name="submitted" value="true">
<div align="center">
<button type="submit">Log In</button>
</div>
</form>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

13
web/logout.php Normal file
View file

@ -0,0 +1,13 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
$_SESSION['logged-in'] = false;
session_destroy();
header('location: login.php');

152
web/logs.php Normal file
View file

@ -0,0 +1,152 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
$page = 1;
$logs_per_page = 10;
if (isset($_GET["page"]) && is_numeric($_GET["page"])) {
$page = intval($_GET["page"]);
}
if (isset($_GET["logs-per-page"]) && is_numeric($_GET["logs-per-page"])) {
$logs_per_page = intval($_GET["logs-per-page"]);
}
/* Are we deleting logs? */
if (isset($_GET["delete-all"]) && !isset($_GET["error"])) {
delete_all_logs();
} else if (isset($_GET["delete-all-on-page"]) && !isset($_GET["error"])) {
$build_logs = get_build_logs(($page - 1) * $logs_per_page, $logs_per_page);
foreach ($build_logs as $log_file) {
$log_number = str_replace(".log", "", basename($log_file));
delete_log($log_number);
}
// Redirect without the "delete-all-on-page" parameter so the user doesn't accidentally do it again by refreshing
$new_parameters = array();
foreach ($_GET as $key => $value) {
if ($key != "delete-all-on-page") {
$new_parameters[$key] = $value;
}
}
redirect_and_die("logs.php", $new_parameters);
} else if (isset($_GET["delete"]) && !isset($_GET["error"])) {
foreach ($_GET["delete"] as $log_number) {
delete_log($log_number);
}
}
$build_logs = get_build_logs(($page - 1) * $logs_per_page, $logs_per_page);
$job_ids = array();
foreach ($build_logs as $log_file) {
// Get the Job ID
$log_file_head = file($log_file);
$job_ids[trim(explode(" ", $log_file_head[1])[1])] = $log_file;
}
unset($build_logs);
krsort($job_ids);
display_header();
display_error_message();
?>
<main>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<div class="card" id="logs">
<div style="width: 100%; overflow: hidden;">
<h2>Build Logs</h2>
<form action="logs.php" method="get" style="display: inline-block;">
<input type="hidden" name="page" value="<?php echo $page; ?>">
<input type="hidden" name="logs-per-page" value="<?php echo $logs_per_page; ?>">
<button type="submit" class="no-decoration">Delete selected logs</button> &nbsp; <a href="logs.php?delete-all=true" class="button-no-decoration">Delete all logs</a>
<br><br>
<input type="checkbox" class="unhide" name="delete-all-on-page" value="true"> Select all
<br>
<?php
$all_selected = "";
$none_selected = "";
foreach ($job_ids as $job_id => $log_file) {
$log_number = str_replace(".log", "", basename($log_file));
$none_selected .= "<input type=\"checkbox\" name=\"delete[]\" value=\"$log_number\"> <a href=\"log.php?log=$log_number\">$job_id</a><br>".PHP_EOL;
$all_selected .= "<input type=\"checkbox\" name=\"delete[]\" value=\"$log_number\" checked disabled> <a href=\"log.php?log=$log_number\">$job_id</a><br>".PHP_EOL;
}
echo "
<div class=\"hidden\">".PHP_EOL.$all_selected."
</div>".PHP_EOL;
echo "
<div class=\"shown\">".PHP_EOL.$none_selected."
</div>".PHP_EOL;
?>
</form>
<!-- List pagination -->
<?php
$logs_count = get_number_of_build_logs();
$pages = ceil($logs_count / $logs_per_page);
$prev_page = $page - 1;
$next_page = $page + 1;
$show_prev = $prev_page >= 1;
$show_next = $next_page <= $pages;
if ($show_prev || $show_next) {
echo "<br><br>Page: ";
if ($show_prev) {
echo "<a href=\"logs.php?page=$prev_page&logs-per-page=$logs_per_page\">&lt;</a> ";
} else {
echo "&lt; ";
}
for ($i = 1; $i <= $pages; $i++) {
if ($i == $page) {
echo "<b>$i</b> ";
} else {
echo "<a href=\"logs.php?page=$i&logs-per-page=$logs_per_page\">$i</a> ";
}
}
if ($show_next) {
echo "<a href=\"logs.php?page=$next_page&logs-per-page=$logs_per_page\">&gt;</a>";
} else {
echo "&gt;";
}
}
?>
<br><br>
<form action="logs.php" method="get" style="display: inline-block;">
<input type="hidden" name="page" value="<?php echo $page; ?>">
Logs per page:
<select name="logs-per-page" id="logs-per-page" class="small">
<?php
for ($i = 10; $i <= 100; $i += 10) {
$selected = $i == $logs_per_page ? " selected" : "";
echo "<option value=\"$i\"$selected>$i</option>";
}
?>
</select>
&nbsp;
<button type="submit" class="no-decoration">Apply</button>
</form>
</div>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

208
web/repositories.php Normal file
View file

@ -0,0 +1,208 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
$debian_repos = get_debian_repos();
$signing_keys = get_signing_keys();
$github_configured = github_is_configured();
if (isset($_GET["action"]) && !isset($_GET["error"])) {
// Form submitted
if (is_array($_GET["action"])) {
redirect_and_die("self");
}
switch ($_GET["action"]) {
case "create":
// Do we have everything we need?
if (empty($_GET["repo_url"])) {
$_GET["error"] = "no-repo-url";
redirect_and_die("repositories.php", $_GET);
}
if (empty($_GET["repo_name"])) {
$_GET["error"] = "no-repo-name";
redirect_and_die("repositories.php", $_GET);
}
$repo_name = $_GET["repo_name"];
$repo_url = $_GET["repo_url"];
$use_existing_key = isset($_GET["use_existing_key"]);
$signing_key = $use_existing_key ? $_GET["signing_key"] : $_GET["key_email"];
$github_pages = isset($_GET["github_pages"]);
$github_pages_url = $github_pages ? $_GET["github_pages_url"] : "";
// Validate input
if (preg_match("/[^a-zA-Z0-9\-\_]/", $repo_name)) {
$_GET["error"] = "invalid-repo-name";
redirect_and_die("repositories.php", $_GET);
}
if (!filter_var($repo_url, FILTER_VALIDATE_URL)
&& !filter_var($repo_url, FILTER_VALIDATE_DOMAIN)
&& !filter_var($repo_url, FILTER_VALIDATE_IP)) {
$_GET["error"] = "invalid-repo-url";
redirect_and_die("repositories.php", $_GET);
}
if (!filter_var($github_pages_url, FILTER_VALIDATE_URL)
&& $github_pages_url != "") {
$_GET["error"] = "invalid-github-pages-url";
redirect_and_die("repositories.php", $_GET);
}
// Generate a new signing key (if we have to)
if (!$use_existing_key) {
create_signing_key($_GET["key_name"], $_GET["key_email"]);
}
// Create the repository
create_debian_repo($repo_name, $repo_url, $signing_key, $github_pages, $github_pages_url);
redirect_and_die("repositories.php");
break;
case "delete":
if (empty($_GET["delete"])) {
$_GET["error"] = "invalid-repo";
redirect_and_die("repositories.php", $_GET);
}
foreach($_GET["delete"] as $repo) {
if (!in_array($repo, $debian_repos)) {
redirect_and_die("repositories.php", array("error" => "invalid-repo"));
}
}
if (isset($_GET["confirm"])) {
delete_debian_repos($_GET["delete"]);
redirect_and_die("repositories.php");
}
break;
}
}
display_header();
display_error_message();
?>
<main>
<?php
if (isset($_GET["delete"]) && !isset( $_GET["confirm"])) {
echo "<div class=\"overlay-container\">
<div class=\"overlay modal\">
<div class=\"modal-content\">
<h2>Confirm Deletion</h2>
<p>Are you sure you want to delete the selected repositories?</p>
<p>Repositories: ";
for ($i = 0; $i < count($_GET["delete"]) - 1; $i++) {
echo $_GET["delete"][$i].", ";
}
echo $_GET["delete"][count($_GET["delete"]) - 1];
echo "</p>
<p>This action is <b>irreversible</b>.</p>
<form action=\"repositories.php\" method=\"get\">
<input type=\"hidden\" name=\"action\" value=\"delete\">
<input type=\"hidden\" name=\"confirm\" value=\"true\">";
foreach ($_GET["delete"] as $repo) {
echo "<input type=\"hidden\" name=\"delete[]\" value=\"$repo\">";
}
echo "<button type=\"submit\">Yes</button>
<a class=\"button\" href=\"repositories.php\">No</a>
</form>
</div>
</div>
</div>".PHP_EOL;
}
?>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<div class="card" id="dashboard">
<?php
if (!empty($debian_repos)) {
echo "<h2>Repositories</h2>
<form action=\"repositories.php\" method=\"get\" style=\"display: inline-block;\">
<input type=\"hidden\" name=\"action\" value=\"delete\">
<button type=\"submit\" class=\"no-decoration\">Delete selected repositories</button>
<br>";
foreach ($debian_repos as $debian_repo) {
echo "<input type=\"checkbox\" name=\"delete[]\" value=\"$debian_repo\" id=\"$debian_repo\"> <label for=\"$debian_repo\">$debian_repo</label><br>".PHP_EOL;
}
echo "
</form>
</div>";
}
?>
<div class="card" id="dashboard">
<h2>Create New Repository</h2>
<form action="repositories.php" method="get" style="display: inline-block;">
<input type="hidden" name="action" value="create">
<h3>Repository Details</h3>
<label for="repo_name">Repository Name:</label>
<input type="text" name="repo_name" placeholder="my-debian-repo"><br>
<label for="repo_url">Repository URL:</label>
<input type="text" name="repo_url" placeholder="https://my.site/deb">
<br><br>
<h3>Signing Key</h3>
<?php
if (!empty($signing_keys)) {
echo '<input type="checkbox" name="use_existing_key" id="use_existing_key" class="unhide" checked>
<label for="use_existing_key"> Use Existing Key</label>
<div class="hidden">
<select name="signing_key">'.PHP_EOL;
foreach ($signing_keys as $key) {
echo "<option value=\"".$key["email"]."\">".$key["name"]." &lt;".$key["email"]."&gt;</option>".PHP_EOL;
}
echo "</select> <a href=\"signing-keys.php\">Manage Keys</a>
</div>";
}
?>
<div class="shown">
<label for="key_name">New Signing Key Name:</label>
<input type="text" name="key_name" placeholder="Example Name">
<br>
<label for="key_email">New Signing Key Email:</label>
<input type="text" name="key_email" placeholder="example@email.com">
</div>
<br>
<h3>Extra</h3>
<input type="checkbox" name="github_pages" id="github_pages" class="unhide2"<?php if (!$github_configured) echo " title=\"Your GitHub credentials are not configured\" disabled"; ?>>
<label for="github_pages"<?php if (!$github_configured) echo " title=\"Your GitHub credentials are not configured\""; ?>>This repository will be served via GitHub Pages</label>
<div class="hidden2">
<label for="github_pages_url">GitHub Pages URL:</label>
<input type="text" name="github_pages_url" placeholder="https://github.com/user/repository.git">
</div>
<br><br>
<button type="submit">Create Repository</button>
</form>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

117
web/settings.autobuild.php Normal file
View file

@ -0,0 +1,117 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
if (isset($_POST["submitted"])) {
// Form submitted
if (!isset($_POST["packages"])
|| !isset($_POST["github-owner"])
|| !isset($_POST["github-email"])
|| !isset($_POST["forgejo-url"])
|| !isset($_POST["forgejo-owner"])) {
$error_params = array("error" => "form-incomplete");
redirect_and_die("settings.autobuild.php", $error_params);
}
$old_config = parse_config();
$new_config = array();
$new_config["packages"] = array();
$new_config["github"] = array();
$new_config["forgejo"] = array();
$package_list = array_filter(explode(PHP_EOL, $_POST["packages"]));
for ($i = 0; $i < count($package_list); $i++) {
$package_list[$i] = trim($package_list[$i]);
$package_list[$i] = filter_var($package_list[$i], FILTER_SANITIZE_URL);
$package_list[$i] = filter_var($package_list[$i], FILTER_SANITIZE_ADD_SLASHES);
}
$new_config["packages"] = $package_list;
unset($package_list);
$new_config["github"]["repo_owner"] = filter_var($_POST["github-owner"], FILTER_SANITIZE_ADD_SLASHES);
$new_config["github"]["email"] = filter_var($_POST["github-email"], FILTER_SANITIZE_EMAIL);
$new_config["forgejo"]["instance_url"] = filter_var(filter_var($_POST["forgejo-url"], FILTER_SANITIZE_URL), FILTER_SANITIZE_ADD_SLASHES);
$new_config["forgejo"]["repo_owner"] = filter_var($_POST["forgejo-owner"], FILTER_SANITIZE_ADD_SLASHES);
if ($_POST["github-token"] != "") {
$new_config["github"]["access_token"] = filter_var($_POST["github-token"], FILTER_SANITIZE_ADD_SLASHES);
} else {
$new_config["github"]["access_token"] = $old_config["github"]["access_token"];
}
if ($_POST["forgejo-token"] != "") {
$new_config["forgejo"]["access_token"] = filter_var($_POST["forgejo-token"], FILTER_SANITIZE_ADD_SLASHES);
} else {
$new_config["forgejo"]["access_token"] = $old_config["forgejo"]["access_token"];
}
update_config($new_config);
}
$config = parse_config();
display_header();
display_error_message();
?>
<main>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<form action="settings.autobuild.php" method="post">
<div class="card" id="packages">
<h2>Packages</h2>
Enter <b>Git URLs</b> to your source packages below (<b>one per line</b>)<br>
<textarea rows="10" style="width: 100%;" name="packages"><?php
foreach ($config["packages"]["package_urls"] as $url) {
echo "$url".PHP_EOL;
}
?></textarea>
</div>
<div class="card" id="github">
<h2>GitHub</h2>
<li><label for="github-owner">GitHub Username: </label><input type="text" name="github-owner" id="github-owner" value="<?php echo $config["github"]["repo_owner"]; ?>"></li>
<br>
<li><label for="github-email">GitHub Email: </label><input type="text" name="github-email" id="github-email" value="<?php echo $config["github"]["email"]; ?>"></li>
<br>
<li><label for="github-token">GitHub Access Token: </label><input type="password" name="github-token" id="github-token" placeholder="Leave blank for no change"></li>
</div>
<div class="card" id="forgejo">
<h2>Forgejo</h2>
<li><label for="forgejo-url">Forgejo Instance URL: </label><input type="text" name="forgejo-url" id="forgejo-url" value="<?php echo $config["forgejo"]["instance_url"]; ?>"></li>
<br>
<li><label for="forgejo-owner">Forgejo Username: </label><input type="text" name="forgejo-owner" id="forgejo-owner" value="<?php echo $config["forgejo"]["repo_owner"]; ?>"></li>
<br>
<li><label for="forgejo-token">Forgejo Access Token: </label><input type="password" name="forgejo-token" id="forgejo-token" placeholder="Leave blank for no change"></li>
</div>
<div class="card" id="submit">
<h2>Save Changes</h2>
<input type="hidden" name="submitted" value="true">
<button type="submit">Save Changes</button>
</div>
</form>
</section>
</div>
</div>
</main>
</body>
</html>

184
web/settings.farm.php Normal file
View file

@ -0,0 +1,184 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
/* Build Farm Settings */
$amd64_configured = vm_is_configured("amd64");
$i386_configured = vm_is_configured("i386");
$arm64_configured = vm_is_configured("arm64");
$amd64_installing = vm_is_installing("amd64");
$i386_installing = vm_is_installing("i386");
$arm64_installing = vm_is_installing("arm64");
$amd64_upgrading = vm_is_upgrading("amd64");
$i386_upgrading = vm_is_upgrading("i386");
$arm64_upgrading = vm_is_upgrading("arm64");
if (isset($_GET["submitted"]) && !isset($_GET["error"])) {
// Form submitted
// Do we have everything we need?
if (!isset($_GET["arch"])) {
redirect_and_die("settings.farm.php", array("error" => "no-arch"));
}
if (!isset($_GET["action"])) {
$_GET["error"] = "no-action";
redirect_and_die("settings.farm.php", $_GET);
}
$arch_list = $_GET["arch"];
$action = $_GET["action"];
// Check if the action is valid
if ($action != "upgrade-vms" && $action != "install-vms" && $action != "uninstall-vms") {
$_GET["error"] = "invalid-action";
redirect_and_die("settings.farm.php", $_GET);
}
foreach ($arch_list as $arch) {
if ($arch != "amd64" && $arch != "i386" && $arch != "arm64") {
$_GET["error"] = "invalid-arch";
redirect_and_die("settings.farm.php", $_GET);
}
if (vm_is_installing($arch)) {
$_GET["error"] = "invalid-action";
redirect_and_die("settings.farm.php", $_GET);
}
if (($action == "install-vms" && vm_is_configured($arch))
|| ($action == "uninstall-vms" && !vm_is_configured($arch))
|| ($action == "upgrade-vms" && !vm_is_configured($arch))) {
$_GET["error"] = "invalid-action";
redirect_and_die("settings.farm.php", $_GET);
}
}
if (isset($_GET["confirm"])) {
switch ($action) {
case "upgrade-vms":
vm_upgrade($arch_list);
break;
case "install-vms":
vm_install($arch_list);
break;
case "uninstall-vms":
vm_uninstall($arch_list);
break;
}
}
}
$amd64_description = ($amd64_configured ? "(Installed)" : "(Not Installed)") . ($amd64_installing ? " [Installing]" : "") . ($amd64_upgrading ? " [Upgrading]" : "");
$i386_description = ($i386_configured ? "(Installed)" : "(Not Installed)") . ($i386_installing ? " [Installing]" : "") . ($i386_upgrading ? " [Upgrading]" : "");
$arm64_description = ($arm64_configured ? "(Installed)" : "(Not Installed)") . ($arm64_installing ? " [Installing]" : "") . ($arm64_upgrading ? " [Upgrading]" : "");
$amd64_checkbox_class = ($amd64_configured ? "installed" : "not-installed") . ($amd64_installing ? " installing" : "");
$i386_checkbox_class = ($i386_configured ? "installed" : "not-installed") . ($i386_installing ? " installing" : "");
$arm64_checkbox_class = ($arm64_configured ? "installed" : "not-installed") . ($arm64_installing ? " installing" : "");
display_header();
display_error_message();
display_note();
?>
<main>
<?php
if (isset($_GET["submitted"]) && !isset( $_GET["confirm"])) {
$action_noun = "Action";
$action_verb = "act";
$extra_info = "";
switch ($_GET["action"]) {
case "upgrade-vms":
$action_noun = "Upgrade";
$action_verb = "upgrade";
$extra_info = "<p>This action may take a long time.</p>";
break;
case "install-vms":
$action_noun = "Installation";
$action_verb = "install";
$extra_info = "<p>This action may take a long time.</p>";
break;
case "uninstall-vms":
$action_noun = "Uninstallation";
$action_verb = "uninstall";
$extra_info = "<p>It may take a long time to re-install the virtual machines.</p>";
break;
}
echo "<div class=\"overlay-container\">
<div class=\"overlay modal\">
<div class=\"modal-content\">
<h2>Confirm $action_noun</h2>
<p>Are you sure you want to $action_verb the selected virtual machines?</p>
<p>Selected: ";
for ($i = 0; $i < count($_GET["arch"]) - 1; $i++) {
echo $_GET["arch"][$i].", ";
}
echo $_GET["arch"][count($_GET["arch"]) - 1];
echo "</p>
$extra_info
<form action=\"settings.farm.php\" method=\"get\">
<input type=\"hidden\" name=\"submitted\" value=\"true\">
<input type=\"hidden\" name=\"action\" value=\"".$_GET["action"]."\">
<input type=\"hidden\" name=\"confirm\" value=\"true\">";
foreach ($_GET["arch"] as $vm) {
echo "<input type=\"hidden\" name=\"arch[]\" value=\"$vm\">";
}
echo "<button type=\"submit\">Yes</button>
<a class=\"button\" href=\"settings.farm.php\">No</a>
</form>
</div>
</div>
</div>".PHP_EOL;
}
?>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<div class="card" id="general">
<h2>Build Farm</h2>
<h3>Architectures</h3>
<form action="settings.farm.php" method="get">
<input type="hidden" name="submitted" value="true">
<div class="multi-button-form">
<input type="checkbox" name="arch[]" id="amd64" value="amd64" class="<?php echo $amd64_checkbox_class; ?>">
<label for="amd64">amd64 <?php echo $amd64_description; ?></label>
<br>
<input type="checkbox" name="arch[]" id="i386" value="i386" class="<?php echo $i386_checkbox_class; ?>">
<label for="i386">i386 <?php echo $i386_description; ?></label>
<br>
<input type="checkbox" name="arch[]" id="arm64" value="arm64" class="<?php echo $arm64_checkbox_class; ?>">
<label for="arm64">arm64 <?php echo $arm64_description; ?></label>
<br><br>
<button type="submit" name="action" value="upgrade-vms" class="button-vm-upgrade">Upgrade Selected</button>
&nbsp;
<button type="submit" name="action" value="install-vms" class="button-vm-install">Install Selected</button>
&nbsp;
<button type="submit" name="action" value="uninstall-vms" class="button-vm-uninstall">Uninstall Selected</button>
</div>
</form>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

54
web/settings.php Normal file
View file

@ -0,0 +1,54 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
/* Settings Portal */
display_header();
display_error_message();
?>
<main>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<div class="mini-card-label">
<h1>Settings</h1>
</div>
<br>
<div class="mini-card-container">
<a class="card mini-card" href="settings.web.php">
<h3>Web</h3>
<img src="img/web.svg" alt="autobuild-web">
Web UI Configuration
</a>
<a class="card mini-card" href="settings.autobuild.php">
<h3>Autobuild</h3>
<img src="img/config.svg" alt="autobuild">
Packages, GitHub, Forgejo
</a>
<a class="card mini-card" href="settings.farm.php">
<h3>Build Farm</h3>
<img src="img/vm.svg" alt="Build Farm">
VM Configuration
</a>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

247
web/settings.web.php Normal file
View file

@ -0,0 +1,247 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
/* Autobuild-web Settings */
if (isset($_GET["action"]) && !isset($_GET["error"])) {
// Form submitted
if (is_array($_GET["action"])) {
redirect_and_die("self");
}
switch ($_GET["action"]) {
case "clear-builds":
run_autobuild("-r all");
redirect_and_die("settings.web.php");
break;
case "clear-logs":
delete_all_logs();
redirect_and_die("settings.web.php");
break;
case "clear-stats":
clear_build_stats();
redirect_and_die("settings.web.php");
break;
case "update-cron":
$auto_clear_builds = isset($_GET["auto-clear-builds"]) ? "1" : "0";
$auto_clear_logs = isset($_GET["auto-clear-logs"]) ? "1" : "0";
$auto_upgrade_vms = isset($_GET["auto-upgrade-vms"]) ? "1" : "0";
$auto_clear_builds_minutes = $_GET["auto-clear-builds-length"];
$auto_clear_logs_minutes = $_GET["auto-clear-logs-length"];
$auto_upgrade_vms_minutes = $_GET["auto-upgrade-vms-cycle"];
if (!is_numeric($auto_clear_builds_minutes) || !is_numeric($auto_clear_logs_minutes) || !is_numeric($auto_upgrade_vms_minutes)) {
redirect_and_die("settings.web.php");
}
switch ($_GET["auto-clear-builds-length-unit"]) {
case "hours":
$auto_clear_builds_minutes = $auto_clear_builds_minutes * 60;
break;
case "days":
$auto_clear_builds_minutes = $auto_clear_builds_minutes * 60 * 24;
break;
}
switch ($_GET["auto-clear-logs-length-unit"]) {
case "hours":
$auto_clear_logs_minutes = $auto_clear_logs_minutes * 60;
break;
case "days":
$auto_clear_logs_minutes = $auto_clear_logs_minutes * 60 * 24;
break;
}
switch ($_GET["auto-upgrade-vms-cycle-unit"]) {
case "hours":
$auto_upgrade_vms_minutes = $auto_upgrade_vms_minutes * 60;
break;
case "days":
$auto_upgrade_vms_minutes = $auto_upgrade_vms_minutes * 60 * 24;
break;
}
$new_cron_settings = array();
$new_cron_settings["auto_clear_builds"] = intval($auto_clear_builds);
$new_cron_settings["auto_clear_logs"] = intval($auto_clear_logs);
$new_cron_settings["auto_upgrade_vms"] = intval($auto_upgrade_vms);
$new_cron_settings["auto_clear_builds_minutes"] = intval($auto_clear_builds_minutes);
$new_cron_settings["auto_clear_logs_minutes"] = intval($auto_clear_logs_minutes);
$new_cron_settings["auto_upgrade_vms_minutes"] = intval($auto_upgrade_vms_minutes);
update_cron_settings($new_cron_settings);
redirect_and_die("settings.web.php");
break;
}
} else if (isset($_POST["action"]) & $_POST["action"] == "update-account") {
$username = $_POST["username"];
$current_password = $_POST["current-password"];
$password = $_POST["password"];
$password_confirm = $_POST["password-confirm"];
if (empty($username) || empty($current_password) || empty($password)) {
redirect_and_die("settings.web.php", array("error" => "form-incomplete"));
}
if (!log_in(get_username(), $current_password)) {
redirect_and_die("settings.web.php", array("error" => "invalid-credentials"));
}
if ($password == $password_confirm) {
update_user($username, $password);
} else {
redirect_and_die("settings.web.php", array("error" => "password-mismatch"));
}
redirect_and_die("settings.web.php");
}
$cron_settings = get_cron_settings();
$auto_clear_builds_checked = $cron_settings["auto_clear_builds"] == "1" ? " checked" : "";
$auto_clear_logs_checked = $cron_settings["auto_clear_logs"] == "1" ? " checked" : "";
$auto_upgrade_vms_checked = $cron_settings["auto_upgrade_vms"] == "1" ? " checked" : "";
$auto_clear_builds_minutes = $cron_settings["auto_clear_builds_minutes"];
$auto_clear_logs_minutes = $cron_settings["auto_clear_logs_minutes"];
$auto_upgrade_vms_minutes = $cron_settings["auto_upgrade_vms_minutes"];
if ($auto_clear_builds_minutes % 1440 == 0) {
$auto_clear_builds_minutes = $auto_clear_builds_minutes / 1440;
$auto_clear_builds_unit = "days";
} elseif ($auto_clear_builds_minutes % 60 == 0) {
$auto_clear_builds_minutes = $auto_clear_builds_minutes / 60;
$auto_clear_builds_unit = "hours";
} else {
$auto_clear_builds_unit = "minutes";
}
if ($auto_clear_logs_minutes % 1440 == 0) {
$auto_clear_logs_minutes = $auto_clear_logs_minutes / 1440;
$auto_clear_logs_unit = "days";
} elseif ($auto_clear_logs_minutes % 60 == 0) {
$auto_clear_logs_minutes = $auto_clear_logs_minutes / 60;
$auto_clear_logs_unit = "hours";
} else {
$auto_clear_logs_unit = "minutes";
}
if ($auto_upgrade_vms_minutes % 1440 == 0) {
$auto_upgrade_vms_minutes = $auto_upgrade_vms_minutes / 1440;
$auto_upgrade_vms_unit = "days";
} elseif ($auto_upgrade_vms_minutes % 60 == 0) {
$auto_upgrade_vms_minutes = $auto_upgrade_vms_minutes / 60;
$auto_upgrade_vms_unit = "hours";
} else {
$auto_upgrade_vms_unit = "minutes";
}
$units_options_array = array(
"minutes" => "<option value=\"minutes\"%selected%>Minutes</option>",
"hours" => "<option value=\"hours\"%selected%>Hours</option>",
"days" => "<option value=\"days\"%selected%>Days</option>"
);
display_header();
display_error_message();
?>
<main>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<div class="card" id="general">
<h2>General</h2>
<div class="two-by-two">
<a class="button" href="?action=clear-builds">Clear Builds Folder</a> &nbsp; <a class="button" href="?action=clear-logs">Clear Logs Folder</a> &nbsp; <a class="button" href="?action=clear-stats">Clear Build Statistics</a>
</div>
<br><br>
<form action="settings.web.php" method="get">
<h3>Old Builds</h3>
<input type="hidden" name="submitted" value="true">
<input type="hidden" name="action" value="update-cron">
<li><input type="checkbox" name="auto-clear-builds" id="auto-clear-builds"<?php echo $auto_clear_builds_checked; ?>> <label for="auto-clear-builds">Auto-delete <u>builds</u> older than &nbsp; </label>
<input type="number" name="auto-clear-builds-length" class="inline" min="0" value="<?php echo $auto_clear_builds_minutes; ?>">
<select name="auto-clear-builds-length-unit" class="inline">
<?php
foreach ($units_options_array as $unit => $option) {
$selected = $auto_clear_builds_unit == $unit ? " selected" : "";
echo str_replace("%selected%", $selected, $option);
}
?>
</select>
</li>
<br>
<h3>Old Logs</h3>
<li><input type="checkbox" name="auto-clear-logs" id="auto-clear-logs"<?php echo $auto_clear_logs_checked; ?>> <label for="auto-clear-logs">Auto-delete <u>logs</u> older than &nbsp; </label>
<input type="number" name="auto-clear-logs-length" class="inline" min="0" value="<?php echo $auto_clear_logs_minutes; ?>">
<select name="auto-clear-logs-length-unit" class="inline">
<?php
foreach ($units_options_array as $unit => $option) {
$selected = $auto_clear_logs_unit == $unit ? " selected" : "";
echo str_replace("%selected%", $selected, $option);
}
?>
</select>
</li>
<br><br>
<h3>Build Farm</h3>
<li><input type="checkbox" name="auto-upgrade-vms" id="auto-upgrade-vms"<?php echo $auto_upgrade_vms_checked; ?>> <label for="auto-upgrade-vms">Auto-upgrade VMs every &nbsp; </label>
<input type="number" name="auto-upgrade-vms-cycle" class="inline" min="0" value="<?php echo $auto_upgrade_vms_minutes; ?>">
<select name="auto-upgrade-vms-cycle-unit" class="inline">
<?php
foreach ($units_options_array as $unit => $option) {
$selected = $auto_upgrade_vms_unit == $unit ? " selected" : "";
echo str_replace("%selected%", $selected, $option);
}
?>
</select>
</li>
<br><br>
<button type="submit" name="update-cron">Save Changes</button>
</form>
</div>
<div class="card" id="account">
<h2>Username/Password</h2>
<form action="settings.web.php" method="post">
<input type="hidden" name="submitted" value="true">
<input type="hidden" name="action" value="update-account">
<label for="username">Username: </label>
<input type="text" name="username" placeholder="Username" value="<?php echo get_username(); ?>">
<br>
<label for="current-password">Current Password: </label>
<input type="password" name="current-password" placeholder="Current Password">
<br>
<br>
<label for="password">New Password: </label>
<input type="password" name="password" placeholder="New Password">
<br>
<label for="password-confirm">Confirm New Password: </label>
<input type="password" name="password-confirm" placeholder="Confirm New Password">
<br>
<button type="submit" name="change-credentials">Save Changes</button>
</form>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

66
web/setup.php Normal file
View file

@ -0,0 +1,66 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
if (isset($_POST["submitted"])) {
// Form submitted, create a new user account
$username = $_POST["username"];
$password = $_POST["password"];
$password_confirm = $_POST["password-confirm"];
if ($password != $password_confirm) {
$params["error"] = "password-mismatch";
redirect_and_die("setup.php", $params);
}
if ($username == "" || $password == "") {
$params["error"] = "form-incomplete";
redirect_and_die("setup.php", $params);
}
unset($password_confirm); // Maybe unnecessary
$password = password_hash($password, PASSWORD_DEFAULT);
if (!create_user($username, $password)) {
$params["error"] = "db-error";
redirect_and_die("setup.php", $params);
}
redirect_and_die("index.php");
}
display_header();
display_error_message();
?>
<main>
<div class="container">
<div class="content-wrapper">
<section class="main-content">
<div class="card" id="dashboard" style="margin-left: 25%; width: 50%;">
<h2>Set Up</h2>
<p>How would you like to sign in?</p>
<br>
<form action="setup.php" method="post">
<input type="text" name="username" placeholder="Enter a new username">
<input type="password" name="password" placeholder="Enter a new password">
<input type="password" name="password-confirm" placeholder="Confirm your new password">
<input type="hidden" name="submitted" value="true">
<div align="center">
<button type="submit">Create account</button>
</div>
</form>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

150
web/signing-keys.php Normal file
View file

@ -0,0 +1,150 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
$signing_keys = get_signing_keys();
if (isset($_GET["action"]) && !isset($_GET["error"])) {
// Form submitted
if (is_array($_GET["action"])) {
redirect_and_die("self");
}
switch ($_GET["action"]) {
case "create":
// Do we have everything we need?
if (empty($_GET["key_email"])) {
redirect_and_die("signing-keys.php", array("error" => "form-incomplete"));
}
if (empty($_GET["key_name"])) {
redirect_and_die("signing-keys.php", array("error" => "form-incomplete"));
}
$key_email = $_GET["key_email"];
$key_name = $_GET["key_name"];
// Validate input
if (!filter_var($key_email, FILTER_VALIDATE_EMAIL)) {
redirect_and_die("signing-keys.php", array("error" => "invalid-email"));
}
// Create the key
create_signing_key($key_name, $key_email);
redirect_and_die("signing-keys.php");
break;
case "delete":
if (isset($_GET["confirm"])) {
foreach ($_GET["delete"] as $key) {
delete_signing_key($key);
}
redirect_and_die("signing-keys.php");
}
break;
}
}
display_header();
display_error_message();
?>
<main>
<?php
if (isset($_GET["delete"]) && !isset( $_GET["confirm"])) {
echo "<div class=\"overlay-container\">
<div class=\"overlay modal\">
<div class=\"modal-content\">
<h2>Confirm Deletion</h2>
<p>Are you sure you want to delete the selected keys?</p>
<p>Keys: ";
for ($i = 0; $i < count($_GET["delete"]) - 1; $i++) {
echo $_GET["delete"][$i].", ";
}
echo $_GET["delete"][count($_GET["delete"]) - 1];
echo "</p>
<p>This action is <b>irreversible</b>.</p>
<form action=\"signing-keys.php\" method=\"get\">
<input type=\"hidden\" name=\"action\" value=\"delete\">
<input type=\"hidden\" name=\"confirm\" value=\"true\">";
foreach ($_GET["delete"] as $key) {
echo "<input type=\"hidden\" name=\"delete[]\" value=\"$key\">";
}
echo "<button type=\"submit\">Yes</button>
<a class=\"button\" href=\"signing-keys.php\">No</a>
</form>
</div>
</div>
</div>".PHP_EOL;
}
?>
<div class="container">
<div class="content-wrapper">
<aside class="sidebar">
<?php
display_sidebar_actions();
display_sidebar_statistics();
?>
</aside>
<section class="main-content">
<div class="card" id="dashboard">
<?php
if (!empty($signing_keys)) {
echo "<h2>Signing Keys</h2>
<form action=\"signing-keys.php\" method=\"get\" style=\"display: inline-block;\">
<input type=\"hidden\" name=\"action\" value=\"delete\">
<button type=\"submit\" class=\"no-decoration\">Delete selected keys</button>
<br>";
foreach ($signing_keys as $signing_key) {
$checkbox_disabled = "";
$in_use_message = "";
if (signing_key_is_in_use($signing_key["email"])) {
$checkbox_disabled = " disabled";
$in_use_message = " &nbsp; <i>(in use by: ";
$repos = get_repos_which_use_signing_key($signing_key["email"]);
for ($i = 0; $i < count($repos) - 1; $i++) {
$in_use_message .= $repos[$i].", ";
}
$in_use_message .= $repos[count($repos) - 1].")</i>";
}
echo "<input type=\"checkbox\" name=\"delete[]\" value=\"".$signing_key["email"]."\" id=\"".$signing_key["email"]."\"$checkbox_disabled> ";
echo "<label for=\"".$signing_key["email"]."\">".$signing_key["name"]." &lt;".$signing_key["email"]."&gt;</label>$in_use_message<br>".PHP_EOL;
}
echo "
</form>
</div>";
}
?>
<div class="card" id="dashboard">
<h2>Create New Signing Key</h2>
<form action="signing-keys.php" method="get" style="display: inline-block;">
<input type="hidden" name="action" value="create">
<h3>Key Details</h3>
<label for="key_name">Key Name:</label>
<input type="text" name="key_name" placeholder="my-signing-key"><br>
<label for="key_email">Key Email:</label>
<input type="text" name="key_email" placeholder="signing-key@email.com">
<br><br>
<button type="submit">Create Key</button>
</form>
</div>
</section>
</div>
</div>
</main>
</body>
</html>

495
web/style.css Normal file
View file

@ -0,0 +1,495 @@
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GPL 3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
line-height: 1.6;
color: #333;
background-color: #f4f4f4;
min-height: 100vh;
}
a {
color: #0066cc;
text-decoration: none;
}
a.no-highlight {
color: #ffffff !important;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Layout */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 20px;
}
/* Header */
header {
background-color: #2c3e50;
color: white;
padding: 1rem 0;
}
.header-content {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
font-size: 1.5rem;
font-weight: bold;
}
/* Navigation */
nav ul {
list-style-type: none;
display: flex;
flex-wrap: wrap;
font-size: 1rem;
padding: 0;
margin: 0;
}
nav ul li {
margin-left: 1rem;
}
nav ul li a {
color: white;
}
/* Error messages */
.error-message {
display: flex;
justify-content: center;
align-items: center;
background-color: #ff0000;
color: #ffffff;
font-size: 1.3rem;
}
.warning-message {
display: flex;
justify-content: center;
align-items: center;
background-color: #ff9500;
color: #ffffff;
font-size: 1.3rem;
}
.note-message {
display: flex;
justify-content: center;
align-items: center;
background-color: #00ff00;
color: #000000;
font-size: 1.3rem;
}
/* Main content */
main {
padding: 2rem 0;
}
.content-wrapper {
display: flex;
gap: 2rem;
}
.sidebar {
flex: 1;
}
.main-content {
flex: 3;
}
.mini-card-container {
display: flex;
column-gap: 3%;
}
.overlay-container {
position: relative;
}
.overlay {
position: fixed;
display: block;
width: fit-content;
height: fit-content;
top: 35%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(0, 0, 0, 0.301);
z-index: 2;
cursor: pointer;
}
.overlay-container ~ .container {
filter: blur(5px);
}
.modal {
background-color: white;
border-radius: 4px;
box-shadow: 4px 4px 4px rgba(0, 0, 0, 0.6);
padding: 1rem;
}
.modal-content {
display: grid;
row-gap: 1rem;
}
/* Cards */
.card {
background-color: white;
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 1rem;
margin-bottom: 1rem;
}
.card.mini-card {
width: 35%;
}
a.mini-card, a.mini-card:visited, a.mini-card:active {
color: inherit;
text-decoration: none;
text-align: center;
}
a.mini-card:hover {
filter: brightness(120%);
}
.mini-card-label {
text-align: center;
}
.card#login {
position: absolute;
left: 50%;
width: 25%;
transform: translate(-50%, 0);
}
.card h2 {
margin-bottom: 0.5rem;
color: #2c3e50;
}
.card h3 {
margin-bottom: 0.5rem;
color: #2c3e50;
}
/* Forms */
form {
display: grid;
row-gap: 1rem;
}
li {
list-style-type: none;
}
.checkbox-list {
display: grid;
grid-template-columns: auto auto;
column-gap: 1px;
}
.two-by-two {
display: flex;
column-gap: 1px;
}
.three-by-three {
display: grid;
grid-template-columns: auto auto auto;
column-gap: 1px;
}
input[type='checkbox'] {
width: 15px;
height: 15px;
}
input[type='checkbox']:disabled {
pointer-events: auto;
}
.hidden, .hidden2 {
display: none;
}
.shown, .shown2 {
display: block;
}
.unhide:checked ~ .hidden,
.unhide2:checked ~ .hidden2 {
display: block;
}
.unhide:checked ~ .shown,
.unhide2:checked ~ .shown2 {
display: none;
}
.not-installed:checked ~ .button-vm-upgrade,
.not-installed:checked ~ .button-vm-uninstall,
.installing:checked ~ .button-vm-upgrade,
.installing:checked ~ .button-vm-uninstall,
.installing:checked ~ .button-vm-install,
.installed:checked ~ .button-vm-install {
background-color: #ccc;
color: #666;
pointer-events: none;
}
label {
font-weight: bold;
}
input[type="text"],
input[type="url"],
input[type="password"],
select {
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
}
input.captcha {
width: 28%;
vertical-align: top;
}
iframe.captcha {
width: calc(160px + 1rem);
height: 50px;
position: relative;
float: right;
border: none;
scrollbar-width: 0;
}
select.inline {
max-width: fit-content;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
}
input[type="number"].inline {
width: 4.5rem;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
}
button {
background-color: #2c3e50;
color: white;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
}
a.button {
background-color: #2c3e50;
color: white;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
text-align: center;
text-decoration: none;
}
a.button:hover {
filter: brightness(120%);
}
a.button-no-decoration:hover {
filter: brightness(120%);
}
button:hover {
background-color: #34495e;
}
button.no-decoration {
background: none!important;
border: none;
padding: 0!important;
color: #069;
text-decoration: underline;
cursor: pointer;
}
button.no-decoration:hover {
filter: brightness(120%);
}
a.button-no-decoration {
background: none!important;
border: none;
padding: 0!important;
color: #069;
text-decoration: underline;
cursor: pointer;
}
.tabs {
display: flex;
padding-bottom: 0;
column-gap: 1px;
row-gap: 2px;
flex-wrap: wrap;
margin-left: 0.5rem;
}
a.tab {
padding: 5px 7px;
background: #e5e5e5;
color: #104563;
font-weight: bold;
font-size: 12px;
transition: background .3s, color .3s;
border:#000000;
border-radius: 1px;
text-align: center;
}
a.tab:hover {
background: #f0f0f0;
text-decoration: none;
}
a.tab.active {
background: #2c3e50;
color: white;
}
select.small {
width: 25%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
}
/* Tables */
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 0.5rem;
text-align: left;
border-bottom: 1px solid #ccc;
}
th {
background-color: #f0f0f0;
font-weight: bold;
}
/* Footer */
footer {
background-color: #2c3e50;
color: white;
padding: 1rem 0;
margin-top: 2rem;
}
.footer-content {
display: flex;
justify-content: space-between;
}
#build-log-iframe {
border: none;
}
#control-buttons {
display: inline-block;
}
#control-buttons :hover {
filter: brightness(120%);
}
/* Responsive design */
@media (max-width: 768px) {
.content-wrapper {
flex-direction: column;
}
.header-content {
flex-direction: column;
align-items: flex-start;
}
nav ul {
margin-top: 1rem;
}
nav ul li {
margin-left: 0;
margin-right: 1rem;
}
.card#login {
width: 75%;
}
.mini-card-container {
flex-direction: column;
}
.card.mini-card {
width: 75%;
align-self: center;
}
.overlay {
width: 75%;
}
.multi-button-form button {
display: flex;
flex-direction: column;
}
.sidebar {
display: none;
}
}

21
web/view-captcha.php Normal file
View file

@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>View Captcha</title>
<link rel="stylesheet" type="text/css" href="style.css">
<style>
.captcha-refresh {
width: 15px;
height: 15px;
vertical-align: 50%;
}
body {
background-color: #ffffff;
}
</style>
</head>
<body>
<img src="captcha.php" alt="Captcha"> <a href="view-captcha.php" class="button-no-decoration"><img src="img/refresh.webp" alt="Refresh" class="captcha-refresh"></a>
</body>
</html>

53
web/view-log.php Normal file
View file

@ -0,0 +1,53 @@
<?php
/**
* autobuild-web
* Copyright (C) 2024 rail5
* This is free software (GNU Affero GPL v3), and you are permitted to redistribute it under certain conditions
* Please see the LICENSE file for more information
*/
require_once "global.php";
if (!isset($_GET["log"])) {
redirect_and_die("index.php");
}
$log_file = "";
if (!isset($_GET["tab"]) || $_GET["tab"] == "main") {
$log_file = get_log_file($_GET["log"]);
} else {
$log_file = get_package_build_log($_GET["log"], $_GET["tab"]);
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Log</title>
<style>
pre {
height: auto;
max-width: 800px;
overflow: auto;
background-color: #eeeeee;
word-break: break-all !important;
word-wrap: break-word !important;
white-space: pre-wrap !important;
}
</style>
</head>
<body>
<pre>
<?php
$build_log = file_get_contents($log_file);
$build_log = htmlentities($build_log);
echo $build_log;
?>
</pre>
<!-- For auto-focusing on the bottom -->
<div id="end"></div>
</body>
</html>

View file

@ -0,0 +1,9 @@
Alias /autobuild /usr/share/autobuild-web/autobuild
<Directory /usr/share/autobuild-web/autobuild>
<FilesMatch \.php$>
<If "-f %{REQUEST_FILENAME}">
SetHandler "proxy:unix:/run/php/autobuild-web.sock|fcgi://localhost"
</If>
</FilesMatch>
</Directory>

View file

@ -0,0 +1,13 @@
location /autobuild {
root /usr/share/autobuild-web;
index index.php index.html index.htm;
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/run/php/autobuild-web.sock;
fastcgi_index index.php;
include /etc/nginx/fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
try_files $uri $uri/ =404;
}

View file

@ -0,0 +1,13 @@
[autobuild-web]
user = _autobuild
listen = /run/php/autobuild-web.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
chdir = /
php_value[session.save_path] = /var/autobuild/web/sessions