Compare commits
165 commits
Author | SHA1 | Date | |
---|---|---|---|
64f2103fb0 | |||
d9283c8294 | |||
8973f9e9bb | |||
66009df10a | |||
1560e028dc | |||
882172774d | |||
9b8906fe82 | |||
2d5ee82731 | |||
fb305a3010 | |||
a39e7a2cdf | |||
5a816df980 | |||
bb149fbb27 | |||
e47b6028b2 | |||
ca0d6fb0ed | |||
2829a70437 | |||
f2995b0261 | |||
1f2d4ec8d7 | |||
937b2395bb | |||
e493947f77 | |||
a03b0721d3 | |||
52de550a39 | |||
e73c5329cc | |||
d692d5577f | |||
dae43e9a44 | |||
f2c7ad6f13 | |||
2f91860de1 | |||
db10aa96b7 | |||
8269e0234a | |||
2e60412edb | |||
a2346d1062 | |||
896e687165 | |||
4ebb8f4093 | |||
f282df6f71 | |||
782f1ff118 | |||
65088da4a9 | |||
9a9d42282d | |||
fcf3458058 | |||
afaf0a55cb | |||
f86f258e4c | |||
0be3628c46 | |||
db3f8b2e3f | |||
c099a8245b | |||
faaa190bac | |||
af2e139357 | |||
5e50287e7c | |||
2bae83d4c7 | |||
027fb0fa34 | |||
ee5336fb81 | |||
9c0a1b0ea9 | |||
bbffebc49d | |||
f989a3860f | |||
c2e30651db | |||
e3edd64275 | |||
9d918507a2 | |||
7390bfac57 | |||
902a47eee0 | |||
ed25168727 | |||
ad44156846 | |||
41a17fe5c4 | |||
cf542aef7c | |||
8de6b1b3b0 | |||
463f8deaaa | |||
ae59978fd0 | |||
685d26b423 | |||
9d93f84b41 | |||
99df4bc1eb | |||
3cc4c30c45 | |||
6b5c824de4 | |||
f7e26d018e | |||
6209fbe94e | |||
8a9377cc11 | |||
1c9bb0a3d8 | |||
3fc3a558bb | |||
c90fc5410f | |||
f657571d17 | |||
f26eab86cb | |||
21612ecb2b | |||
f16dec7ec7 | |||
06ae936c68 | |||
2560df4426 | |||
11bd80d8bd | |||
a3f7ad9821 | |||
8383b9e9d0 | |||
52669bb31d | |||
c663a9db77 | |||
89a25894fd | |||
7dd9d23dc4 | |||
c0afaba57e | |||
378044afaa | |||
4fb87fd331 | |||
7865c06d41 | |||
5115ef1766 | |||
98407f5777 | |||
3890ac4564 | |||
0e30e5f8b1 | |||
cb282efdca | |||
0fddca024d | |||
15f204b772 | |||
5752c0e0ef | |||
dfd1df703d | |||
bb41ee5a39 | |||
04e84fea09 | |||
0dcd1cdb99 | |||
3b2987f007 | |||
9c653c6fb8 | |||
1ca83588ac | |||
b2e3dfbc3e | |||
81072d5306 | |||
84fc1790ae | |||
9c5bdd8ee6 | |||
9d49eea874 | |||
f88927b9fa | |||
404421fe45 | |||
99c506e257 | |||
f084c62608 | |||
fb3eef64dc | |||
0d95266d9a | |||
70b066abff | |||
ed7ca74df0 | |||
5d253b5f9e | |||
f22d62bd27 | |||
b2e2d91d8b | |||
cb2a668086 | |||
b040533782 | |||
6de5fa4729 | |||
b44b4ebff7 | |||
5ffbd6cf80 | |||
fe31a27d8e | |||
270b41f994 | |||
6c15586d1e | |||
9ca91bac24 | |||
cb999e2ddd | |||
045d0ff9ab | |||
2c46d36398 | |||
cb4dc0a288 | |||
17ba2624ce | |||
c7dcb8dc5d | |||
ada3de254c | |||
3b90aa9a7b | |||
cff30b857f | |||
d72c103b58 | |||
22d3f79f99 | |||
6707fa8c59 | |||
319c2f448f | |||
cd8625e28c | |||
970b2f78b5 | |||
988fb562b7 | |||
0d129534a9 | |||
de3a2355dc | |||
ead6435dbc | |||
c7cffc19bc | |||
87555d7eb1 | |||
08116c8697 | |||
71c117a3b9 | |||
4b88aa0b31 | |||
736313e53e | |||
c05064d093 | |||
9706d1e671 | |||
b5dc480218 | |||
d8a0ac2d98 | |||
604cfa525a | |||
cdd75ff788 | |||
40fa11f448 | |||
35ba3a2689 | |||
0b72cc7403 |
52 changed files with 6683 additions and 192 deletions
41
README.md
41
README.md
|
@ -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"> |
|
||||
|
||||
|
|
12
autobuild
12
autobuild
|
@ -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
|
||||
|
|
600
autobuild-setup
600
autobuild-setup
|
@ -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
|
||||
;;
|
||||
|
|
810
autobuildd
810
autobuildd
File diff suppressed because it is too large
Load diff
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
15
config.toml
15
config.toml
|
@ -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
61
debian/autobuild-web.postinst
vendored
Executable 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
40
debian/autobuild-web.postrm
vendored
Executable 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
47
debian/autobuild.postinst
vendored
Executable 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
211
debian/changelog
vendored
|
@ -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
13
debian/control
vendored
|
@ -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
26
debian/postinst
vendored
|
@ -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
10
debian/rules
vendored
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
31
repository/default-index.html
Normal file
31
repository/default-index.html
Normal 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>
|
|
@ -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
255
web/build.php
Normal 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
25
web/captcha.php
Normal 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
60
web/download.php
Normal 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
501
web/global.config.php
Normal 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
33
web/global.cron.php
Normal 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
169
web/global.db.php
Normal 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
180
web/global.display.php
Normal 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
69
web/global.general.php
Normal 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
36
web/global.job.php
Normal 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
287
web/global.log.php
Normal 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
23
web/global.php
Normal 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
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
252
web/img/config.svg
Normal 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 <a href="http://tango.freedesktop.org/Tango_Desktop_Project"> Tango Project </a> \n<br><br> \nSince version 0.8.90 Tango Project icons are Public Domain: <a href="http://tango.freedesktop.org/Frequently_Asked_Questions#Terms_of_Use.3F"> Tango Project FAQ </a></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
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
279
web/img/vm.svg
Normal 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
687
web/img/web.svg
Normal 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 <a href="http://tango.freedesktop.org/Tango_Desktop_Project"> Tango Project </a> \n<br><br> \nSince version 0.8.90 Tango Project icons are Public Domain: <a href="http://tango.freedesktop.org/Frequently_Asked_Questions#Terms_of_Use.3F"> Tango Project FAQ </a></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
34
web/index.php
Normal 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
116
web/log.php
Normal 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>
|
||||
|
||||
<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
69
web/login.php
Normal 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
13
web/logout.php
Normal 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
152
web/logs.php
Normal 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> <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\"><</a> ";
|
||||
} else {
|
||||
echo "< ";
|
||||
}
|
||||
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\">></a>";
|
||||
} else {
|
||||
echo ">";
|
||||
}
|
||||
}
|
||||
?>
|
||||
<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>
|
||||
|
||||
<button type="submit" class="no-decoration">Apply</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
208
web/repositories.php
Normal file
208
web/repositories.php
Normal 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"]." <".$key["email"]."></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
117
web/settings.autobuild.php
Normal 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
184
web/settings.farm.php
Normal 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>
|
||||
|
||||
<button type="submit" name="action" value="install-vms" class="button-vm-install">Install Selected</button>
|
||||
|
||||
<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
54
web/settings.php
Normal 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
247
web/settings.web.php
Normal 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> <a class="button" href="?action=clear-logs">Clear Logs Folder</a> <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 </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 </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 </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
66
web/setup.php
Normal 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
150
web/signing-keys.php
Normal 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 = " <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"]." <".$signing_key["email"]."></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
495
web/style.css
Normal 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
21
web/view-captcha.php
Normal 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
53
web/view-log.php
Normal 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>
|
9
webconf/apache/autobuild-web.conf
Normal file
9
webconf/apache/autobuild-web.conf
Normal 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>
|
13
webconf/nginx/autobuild-web.conf
Normal file
13
webconf/nginx/autobuild-web.conf
Normal 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;
|
||||
}
|
13
webconf/php-fpm/autobuild-web.conf
Normal file
13
webconf/php-fpm/autobuild-web.conf
Normal 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
|
Loading…
Reference in a new issue