init
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
tmp/*
|
||||
chromedriver*
|
||||
*.apk
|
||||
@@ -0,0 +1,93 @@
|
||||
FROM golang:1.17 as go
|
||||
|
||||
COPY tmp/devtools /devtools
|
||||
|
||||
RUN \
|
||||
apt-get update && \
|
||||
apt-get install -y upx-ucl libx11-dev && \
|
||||
cd /devtools && \
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||
upx /devtools/devtools
|
||||
|
||||
FROM ubuntu:18.04
|
||||
|
||||
ARG APPIUM_VERSION="1.8.1"
|
||||
|
||||
RUN \
|
||||
apt update && \
|
||||
apt remove -y libcurl4 && \
|
||||
apt install -y apt-transport-https ca-certificates tzdata locales libcurl4 curl gnupg && \
|
||||
curl --silent --location https://deb.nodesource.com/setup_12.x | bash - && \
|
||||
apt install -y --no-install-recommends \
|
||||
curl \
|
||||
iproute2 \
|
||||
nodejs \
|
||||
openjdk-8-jdk-headless \
|
||||
unzip \
|
||||
xvfb \
|
||||
libpulse0 \
|
||||
libxcomposite1 \
|
||||
libxcursor1 \
|
||||
libxi6 \
|
||||
libasound2 \
|
||||
fluxbox \
|
||||
x11vnc \
|
||||
feh \
|
||||
wmctrl \
|
||||
libglib2.0-0 && \
|
||||
apt-get clean && \
|
||||
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||
|
||||
RUN cd / && npm install --prefix ./opt/ appium@$APPIUM_VERSION
|
||||
|
||||
COPY android.conf /etc/ld.so.conf.d/
|
||||
COPY fluxbox/aerokube /usr/share/fluxbox/styles/
|
||||
COPY fluxbox/init /root/.fluxbox/
|
||||
COPY fluxbox/aerokube.png /usr/share/images/fluxbox/
|
||||
COPY --from=go /devtools/devtools /usr/bin/
|
||||
|
||||
# Android SDK
|
||||
ENV ANDROID_HOME /opt/android-sdk-linux
|
||||
ENV PATH /opt/android-sdk-linux/platform-tools:/opt/android-sdk-linux/tools:/opt/android-sdk-linux/tools/bin:/opt/android-sdk-linux/emulator:$PATH
|
||||
ENV LD_LIBRARY_PATH ${ANDROID_HOME}/emulator/lib64:${ANDROID_HOME}/emulator/lib64/gles_swiftshader:${ANDROID_HOME}/emulator/lib64/qt/lib:${ANDROID_HOME}/emulator/lib64/vulkan:${LD_LIBRARY_PATH}
|
||||
ENV JAVA_HOME /usr/lib/jvm/java-8-openjdk-amd64
|
||||
|
||||
ARG ANDROID_DEVICE=""
|
||||
ARG REPLACE_IMG="y"
|
||||
ARG AVD_NAME="android6.0-1"
|
||||
ARG BUILD_TOOLS="build-tools;23.0.1"
|
||||
ARG PLATFORM="android-23"
|
||||
ARG EMULATOR_IMAGE="system-images;android-23;default;x86"
|
||||
ARG EMULATOR_IMAGE_TYPE="default"
|
||||
ARG ANDROID_ABI="x86"
|
||||
ARG SDCARD_SIZE="500"
|
||||
ARG USERDATA_SIZE="500"
|
||||
|
||||
RUN \
|
||||
curl -o sdk-tools.zip https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip && \
|
||||
mkdir -p /opt/android-sdk-linux /root/.config/Android\ Open\ Source\ Project/ && \
|
||||
unzip -q sdk-tools.zip -d /opt/android-sdk-linux && \
|
||||
rm sdk-tools.zip && \
|
||||
yes | sdkmanager --licenses
|
||||
|
||||
RUN \
|
||||
sdkmanager "emulator" "tools" "platform-tools" "$BUILD_TOOLS" "platforms;$PLATFORM" "$EMULATOR_IMAGE" && \
|
||||
mksdcard "$SDCARD_SIZE"M sdcard.img && \
|
||||
echo "no" | ( \
|
||||
([ -n "$ANDROID_DEVICE" ] && avdmanager create avd -n "$AVD_NAME" -k "$EMULATOR_IMAGE" --abi "$ANDROID_ABI" --device "$ANDROID_DEVICE" --sdcard /sdcard.img ) || \
|
||||
avdmanager create avd -n "$AVD_NAME" -k "$EMULATOR_IMAGE" --abi "$ANDROID_ABI" --sdcard /sdcard.img \
|
||||
) && \
|
||||
ldconfig && \
|
||||
( \
|
||||
resize2fs /root/.android/avd/$AVD_NAME.avd/userdata.img "$USERDATA_SIZE"M || \
|
||||
/opt/android-sdk-linux/emulator/qemu-img resize -f raw /root/.android/avd/$AVD_NAME.avd/userdata.img "$USERDATA_SIZE"M \
|
||||
) && \
|
||||
([ -z "$REPLACE_IMG" ] || mv /root/.android/avd/$AVD_NAME.avd/userdata.img /root/.android/avd/$AVD_NAME.avd/userdata-qemu.img ) && \
|
||||
rm /opt/android-sdk-linux/system-images/$PLATFORM/$EMULATOR_IMAGE_TYPE/"$ANDROID_ABI"/userdata.img
|
||||
|
||||
COPY ["Emulator.conf", "/root/.config/Android Open Source Project/Emulator.conf"]
|
||||
COPY emulator-snapshot.sh tmp/chromedriver* *.apk /usr/bin/
|
||||
|
||||
# Entrypoint
|
||||
COPY tmp/entrypoint.sh /
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
@@ -0,0 +1,2 @@
|
||||
[General]
|
||||
showNestedWarning=false
|
||||
@@ -0,0 +1,2 @@
|
||||
/opt/android-sdk-linux/emulator/lib64
|
||||
/opt/android-sdk-linux/emulator/lib64/gles_swiftshader
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
MAX_ATTEMPTS=5
|
||||
adb root
|
||||
adb devices | grep emulator | cut -f1 | while read id; do
|
||||
apks=(/usr/bin/*.apk)
|
||||
for apk in "${apks[@]}"; do
|
||||
if [ -r "$apk" ]; then
|
||||
for i in `seq 1 ${MAX_ATTEMPTS}`; do
|
||||
echo "Installing $apk (attempt #$i of $MAX_ATTEMPTS)"
|
||||
adb -s "$id" install -r "$apk" && break || sleep 15 && echo "Retrying to install $apk"
|
||||
done
|
||||
fi
|
||||
done
|
||||
adb -s "$id" emu kill -2 || true
|
||||
done
|
||||
rm -f /tmp/.X99-lock || true
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
CHROMEDRIVER_PORT=9515
|
||||
BOOTSTRAP_PORT=4725
|
||||
EMULATOR=emulator-5554
|
||||
APPIUM_ARGS=${APPIUM_ARGS:-""}
|
||||
EMULATOR_ARGS=${EMULATOR_ARGS:-""}
|
||||
PORT=${PORT:-"4444"}
|
||||
DISPLAY_NUM=99
|
||||
export DISPLAY=":$DISPLAY_NUM"
|
||||
SCREEN_RESOLUTION=${SCREEN_RESOLUTION:-"1920x1080x24"}
|
||||
SKIN=${SKIN:-"1080x1920"}
|
||||
STOP=""
|
||||
VERBOSE=${VERBOSE:-""}
|
||||
|
||||
if [ -z "$VERBOSE" ]; then
|
||||
if [ -z "$APPIUM_ARGS" ]; then
|
||||
APPIUM_ARGS="--log-level error"
|
||||
fi
|
||||
else
|
||||
EMULATOR_ARGS="$EMULATOR_ARGS -verbose"
|
||||
fi
|
||||
|
||||
|
||||
clean() {
|
||||
STOP="yes"
|
||||
if [ -n "$APPIUM_PID" ]; then
|
||||
kill -TERM "$APPIUM_PID"
|
||||
fi
|
||||
if [ -n "$EMULATOR_PID" ]; then
|
||||
kill -TERM "$EMULATOR_PID"
|
||||
fi
|
||||
if [ -n "$X11VNC_PID" ]; then
|
||||
kill -TERM "$X11VNC_PID"
|
||||
fi
|
||||
if [ -n "$DEVTOOLS_PID" ]; then
|
||||
kill -TERM "$DEVTOOLS_PID"
|
||||
fi
|
||||
if [ -n "$XVFB_PID" ]; then
|
||||
kill -TERM "$XVFB_PID"
|
||||
fi
|
||||
}
|
||||
|
||||
trap clean SIGINT SIGTERM
|
||||
|
||||
/usr/bin/xvfb-run -e /dev/stdout -l -n "$DISPLAY_NUM" -s "-ac -screen 0 $SCREEN_RESOLUTION -noreset -listen tcp" /usr/bin/fluxbox -display "$DISPLAY" -log /tmp/fluxbox.log 2>/dev/null &
|
||||
XVFB_PID=$!
|
||||
|
||||
retcode=1
|
||||
until [ $retcode -eq 0 ] || [ -n "$STOP" ]; do
|
||||
DISPLAY="$DISPLAY" wmctrl -m >/dev/null 2>&1
|
||||
retcode=$?
|
||||
if [ $retcode -ne 0 ]; then
|
||||
echo Waiting X server...
|
||||
sleep 0.1
|
||||
fi
|
||||
done
|
||||
if [ -n "$STOP" ]; then exit 0; fi
|
||||
|
||||
if [ "$ENABLE_VNC" != "true" ] && [ "$ENABLE_VIDEO" != "true" ]; then
|
||||
EMULATOR_ARGS="$EMULATOR_ARGS -no-window"
|
||||
fi
|
||||
ANDROID_AVD_HOME=/root/.android/avd DISPLAY="$DISPLAY" /opt/android-sdk-linux/emulator/emulator ${EMULATOR_ARGS} -writable-system -no-boot-anim -no-audio -no-jni -avd @AVD_NAME@ -sdcard /sdcard.img -skin "$SKIN" -skindir /opt/android-sdk-linux/platforms/@PLATFORM@/skins/ -gpu swiftshader_indirect -ranchu -qemu -enable-kvm &
|
||||
EMULATOR_PID=$!
|
||||
|
||||
if [ "$ENABLE_VNC" == "true" ]; then
|
||||
x11vnc -display "$DISPLAY" -passwd selenoid -shared -forever -loop500 -rfbport 5900 -rfbportv6 5900 -logfile /tmp/x11vnc.log &
|
||||
X11VNC_PID=$!
|
||||
fi
|
||||
|
||||
while [ "$(adb shell getprop sys.boot_completed | tr -d '\r')" != "1" ] && [ -z "$STOP" ] ; do sleep 1; done
|
||||
if [ -n "$STOP" ]; then exit 0; fi
|
||||
|
||||
DEFAULT_CAPABILITIES='"androidNaturalOrientation": true, "deviceName": "android", "platformName": "Android", "noReset": true, "udid":"'$EMULATOR'"'
|
||||
if [ -n "@CHROME_MOBILE@" ]; then
|
||||
while ip addr | grep inet | grep -q tentative > /dev/null; do sleep 0.1; done
|
||||
DEFAULT_CAPABILITIES=$DEFAULT_CAPABILITIES', "chromedriverPort": '$CHROMEDRIVER_PORT
|
||||
/usr/bin/devtools --android &
|
||||
DEVTOOLS_PID=$!
|
||||
fi
|
||||
|
||||
if [ -x "/usr/bin/chromedriver" ]; then
|
||||
DEFAULT_CAPABILITIES=$DEFAULT_CAPABILITIES',"chromedriverExecutable": "/usr/bin/chromedriver"'
|
||||
fi
|
||||
|
||||
/opt/node_modules/.bin/appium -a 0.0.0.0 -p "$PORT" -bp "$BOOTSTRAP_PORT" --log-timestamp --log-no-colors --command-timeout 90 --no-reset ${APPIUM_ARGS} --default-capabilities "{$DEFAULT_CAPABILITIES}" &
|
||||
APPIUM_PID=$!
|
||||
|
||||
wait
|
||||
@@ -0,0 +1,175 @@
|
||||
###############################################################################
|
||||
#
|
||||
# name: aerokube (based on ubuntu_light)
|
||||
# made: paultag
|
||||
# date: 16-10-2010
|
||||
# http://pault.ag/
|
||||
#
|
||||
############################################################## BACKGROUND ######
|
||||
|
||||
# background: flat
|
||||
# background.color: #2c001e
|
||||
# background.colorTo: #2c001e
|
||||
|
||||
background: fullscreen
|
||||
background.pixmap: /usr/share/images/fluxbox/aerokube.png
|
||||
|
||||
############################################################## FONTS ##########
|
||||
|
||||
|
||||
menu.frame.font: Ubuntu-10:bold
|
||||
menu.title.font: Ubuntu-12:bold
|
||||
toolbar.clock.font: Ubuntu-10:bold
|
||||
toolbar.workspace.font: Ubuntu-12:bold
|
||||
toolbar.iconbar.focused.font: Ubuntu-10:bold
|
||||
toolbar.iconbar.unfocused.font: Ubuntu-10
|
||||
window.font: Ubuntu-10
|
||||
|
||||
############################################################## MENU ###########
|
||||
|
||||
menu.bevelWidth: 1
|
||||
|
||||
#menu.itemHeight: 35
|
||||
#menu.titleHeight: 21
|
||||
menu.borderColor: #525252
|
||||
menu.borderWidth: 1
|
||||
|
||||
menu.bullet: Triangle
|
||||
menu.bullet.position: Right
|
||||
menu.frame.underlineColor: #ffffff
|
||||
|
||||
menu.title: flat gradient rectangle
|
||||
menu.title.justify: center
|
||||
menu.title.color: #FFFFFF
|
||||
menu.title.colorTo: #FFFFFF
|
||||
menu.title.textColor: #333333
|
||||
|
||||
menu.frame: flat gradient crossdiagonal
|
||||
menu.frame.justify: left
|
||||
menu.frame.color: #FFFFFF
|
||||
menu.frame.colorTo: #FFFFFF
|
||||
menu.frame.textColor: #333333
|
||||
menu.frame.disableColor: #aea79f
|
||||
|
||||
menu.hilite: flat gradient rectangle
|
||||
menu.hilite.color: #aea79f
|
||||
menu.hilite.colorTo: #aea79f
|
||||
menu.hilite.textColor: #000000
|
||||
|
||||
############################################################## TOOLBAR ########
|
||||
|
||||
toolbar.bevelWidth: 0
|
||||
|
||||
toolbar.borderWidth: 1
|
||||
toolbar.borderColor: #aea79f
|
||||
|
||||
toolbar.height: 20
|
||||
|
||||
toolbar.justify: center
|
||||
|
||||
toolbar: flat gradient rectangle
|
||||
toolbar.pixmap:
|
||||
toolbar.color: #aea79f
|
||||
toolbar.colorTo: #aea79f
|
||||
|
||||
toolbar.clock: parentrelative
|
||||
toolbar.clock.justify: center
|
||||
toolbar.clock.color: #
|
||||
toolbar.clock.colorTo: #
|
||||
toolbar.clock.textColor: #333333
|
||||
|
||||
toolbar.workspace: parentrelative
|
||||
toolbar.workspace.justify: Center
|
||||
toolbar.workspace.color: #
|
||||
toolbar.workspace.colorTo: #
|
||||
toolbar.workspace.textColor: #333333
|
||||
|
||||
toolbar.button: parentrelative
|
||||
toolbar.button.color: #333333
|
||||
toolbar.button.colorTo: #333333
|
||||
toolbar.button.picColor: #333333
|
||||
toolbar.button.pressed: parentrelative
|
||||
toolbar.button.pressed.color: #aea79f
|
||||
toolbar.button.pressed.colorTo: #aea79f
|
||||
toolbar.button.pressed.picColor: #000000
|
||||
|
||||
toolbar.iconbar.borderWidth: 1
|
||||
toolbar.iconbar.borderColor: #333333
|
||||
|
||||
toolbar.iconbar.empty: parentrelative
|
||||
toolbar.iconbar.empty.color: #
|
||||
toolbar.iconbar.empty.colorTo: #
|
||||
|
||||
toolbar.iconbar.focused.borderWidth: 1
|
||||
toolbar.iconbar.focused.borderColor: #333333
|
||||
toolbar.iconbar.focused: flat gradient rectangle
|
||||
toolbar.iconbar.focused.color: #FFFFFF
|
||||
toolbar.iconbar.focused.colorTo: #FFFFFF
|
||||
toolbar.iconbar.focused.textColor: #333333
|
||||
toolbar.iconbar.focused.justify: center
|
||||
|
||||
toolbar.iconbar.unfocused.borderWidth: 1
|
||||
toolbar.iconbar.unfocused.borderColor: #525252
|
||||
toolbar.iconbar.unfocused: flat gradient rectangle
|
||||
toolbar.iconbar.unfocused.color: #aea79f
|
||||
toolbar.iconbar.unfocused.colorTo: #aea79f
|
||||
toolbar.iconbar.unfocused.textColor: #444444
|
||||
toolbar.iconbar.unfocused.justify: center
|
||||
|
||||
############################################################## WINDOW #########
|
||||
|
||||
window.roundCorners: TopRight TopLeft
|
||||
window.bevelWidth: 4
|
||||
window.shade: false
|
||||
window.borderWidth: 1
|
||||
window.borderColor: #333333
|
||||
|
||||
window.justify: Center
|
||||
window.title.height: 21
|
||||
|
||||
window.title.focus: flat gradient rectangle
|
||||
window.title.focus.color: #000000
|
||||
window.title.focus.colorTo: #333333
|
||||
window.title.unfocus: flat gradient rectangle
|
||||
window.title.unfocus.color: #111111
|
||||
window.title.unfocus.colorTo: #444444
|
||||
|
||||
window.label.focus: parentrelative
|
||||
window.label.focus.color: #
|
||||
window.label.focus.colorTo: #
|
||||
window.label.focus.textColor: #FFFFFF
|
||||
window.label.unfocus: parentrelative
|
||||
window.label.unfocus.color: #
|
||||
window.label.unfocus.colorTo: #
|
||||
window.label.unfocus.textColor: #aea79f
|
||||
|
||||
window.button.focus: parentrelative
|
||||
window.button.focus.color: #
|
||||
window.button.focus.colorTo: #
|
||||
window.button.focus.picColor: #FFFFFF
|
||||
window.button.unfocus: parentrelative
|
||||
window.button.unfocus.Color: #
|
||||
window.button.unfocus.ColorTo: #
|
||||
window.button.unfocus.picColor: #888888
|
||||
window.button.pressed: parentrelative
|
||||
window.button.pressed.color: #
|
||||
window.button.pressed.colorTo: #
|
||||
window.button.pressed.picColor: #000000
|
||||
|
||||
window.handle.focus: flat
|
||||
window.handle.focus.color: #1f1f1f
|
||||
window.handle.focus.colorTo: #1f1f1f
|
||||
window.handle.unfocus: lat
|
||||
window.handle.unfocus.color: #1f1f1f
|
||||
window.handle.unfocus.colorTo: #1f1f1f
|
||||
window.handleWidth: 2
|
||||
|
||||
window.grip.focus: flat
|
||||
window.grip.focus.color: #525252
|
||||
window.grip.focus.colorTo: #525252
|
||||
window.grip.unfocus: flat
|
||||
window.grip.unfocus.color: #1f1f1f
|
||||
window.grip.unfocus.colorTo: #1f1f1f
|
||||
|
||||
###############################################################################
|
||||
# EOF
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
@@ -0,0 +1,7 @@
|
||||
session.menuFile: ~/.fluxbox/menu
|
||||
session.keyFile: ~/.fluxbox/keys
|
||||
session.styleFile: /usr/share/fluxbox/styles/aerokube
|
||||
session.configVersion: 13
|
||||
session.screen0.strftimeFormat: %d %b, %a %02k:%M:%S
|
||||
session.screen0.toolbar.visible: false
|
||||
session.screen0.toolbar.tools:
|
||||
Executable
+251
@@ -0,0 +1,251 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
die(){
|
||||
echo "$1"
|
||||
return 1
|
||||
}
|
||||
|
||||
require_command(){
|
||||
if [ -z "$(command -v $1)" ]; then
|
||||
die "$1 command required for this script to run"
|
||||
fi
|
||||
}
|
||||
|
||||
request_answer(){
|
||||
prompt=$1
|
||||
default_value=$2
|
||||
if [ -n "$default_value" ]; then
|
||||
prompt="$prompt [$default_value]"
|
||||
fi
|
||||
read -r -p "$prompt " value
|
||||
if [ -z "$value" ] && [ -n "$default_value" ]; then
|
||||
value="$default_value"
|
||||
fi
|
||||
echo "$value"
|
||||
}
|
||||
|
||||
validate_android_version(){
|
||||
version="$1"
|
||||
type=${2:-"default"}
|
||||
abi=${3:-"x86"}
|
||||
avd_name="android$version-1"
|
||||
build_tools="build-tools;29.0.2"
|
||||
replace_img="y"
|
||||
case "$version" in
|
||||
4.4)
|
||||
platform="android-19"
|
||||
emulator_image="system-images;android-19;$type;$abi"
|
||||
;;
|
||||
5.0)
|
||||
platform="android-21"
|
||||
emulator_image="system-images;android-21;$type;$abi"
|
||||
;;
|
||||
5.1)
|
||||
platform="android-22"
|
||||
emulator_image="system-images;android-22;$type;$abi"
|
||||
;;
|
||||
6.0)
|
||||
platform="android-23"
|
||||
emulator_image="system-images;android-23;$type;$abi"
|
||||
;;
|
||||
7.0)
|
||||
platform="android-24"
|
||||
emulator_image="system-images;android-24;$type;$abi"
|
||||
;;
|
||||
7.1)
|
||||
platform="android-25"
|
||||
emulator_image="system-images;android-25;$type;$abi"
|
||||
;;
|
||||
8.0)
|
||||
platform="android-26"
|
||||
emulator_image="system-images;android-26;$type;$abi"
|
||||
;;
|
||||
8.1)
|
||||
platform="android-27"
|
||||
emulator_image="system-images;android-27;$type;$abi"
|
||||
;;
|
||||
9.0)
|
||||
platform="android-28"
|
||||
emulator_image="system-images;android-28;$type;$abi"
|
||||
replace_img=""
|
||||
;;
|
||||
10.0)
|
||||
platform="android-29"
|
||||
emulator_image="system-images;android-29;$type;$abi"
|
||||
replace_img=""
|
||||
;;
|
||||
11.0)
|
||||
platform="android-30"
|
||||
emulator_image="system-images;android-30;$type;$abi"
|
||||
replace_img=""
|
||||
;;
|
||||
12.0)
|
||||
platform="android-31"
|
||||
emulator_image="system-images;android-31;$type;$abi"
|
||||
replace_img=""
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported Android version"
|
||||
false
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
validate_android_image_type(){
|
||||
type="$1"
|
||||
case "$type" in
|
||||
"default" | "google_apis" | "google_apis_playstore" | "android-wear" | "android-tv" )
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported Android image type"
|
||||
false
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
validate_android_abi(){
|
||||
abi="$1"
|
||||
case "$abi" in
|
||||
"armeabi-v7a" | "arm64-v8a" | "x86" | "x86_64" )
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported Application Binary Interface"
|
||||
false
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
download_chromedriver() {
|
||||
pushd "$TMP_DIR"
|
||||
wget -O chromedriver.zip http://chromedriver.storage.googleapis.com/$1/chromedriver_linux64.zip
|
||||
unzip chromedriver.zip
|
||||
rm chromedriver.zip
|
||||
popd
|
||||
}
|
||||
|
||||
test_image(){
|
||||
tests_dir=../../selenoid-container-tests/
|
||||
if [ -d "$tests_dir" ]; then
|
||||
echo "Running test suite on image."
|
||||
docker rm -f selenium || true
|
||||
docker run -d --privileged --name selenium -p 4445:4444 $1
|
||||
echo "Waiting for image to start..."
|
||||
sleep 20
|
||||
pushd "$tests_dir"
|
||||
mvn clean test -Dgrid.connection.url="http://localhost:4445/wd/hub" -Dgrid.browser.name=chrome || true
|
||||
popd
|
||||
docker rm -f selenium || true
|
||||
else
|
||||
echo "Skipping tests as $tests_dir does not exist."
|
||||
fi
|
||||
}
|
||||
|
||||
require_command "docker"
|
||||
require_command "sed"
|
||||
require_command "true"
|
||||
require_command "false"
|
||||
require_command "wget"
|
||||
require_command "unzip"
|
||||
require_command "cut"
|
||||
|
||||
TMP_DIR="android/tmp"
|
||||
rm -Rf ./"$TMP_DIR" || true
|
||||
mkdir -p "$TMP_DIR"
|
||||
cp android/entrypoint.sh "$TMP_DIR/entrypoint.sh"
|
||||
cp -r ../static/chrome/devtools "$TMP_DIR/devtools"
|
||||
|
||||
appium_version=$(request_answer "Specify Appium version:" "1.18.1")
|
||||
|
||||
until [ "$?" -ne 0 ]; do
|
||||
android_image_type=$(request_answer "Specify Android image type (possible values: \"default\", \"google_apis\", \"google_apis_playstore\", \"android-tv\", \"android-wear\"):" "default")
|
||||
if validate_android_image_type "$android_image_type"; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
until [ "$?" -ne 0 ]; do
|
||||
android_abi=$(request_answer "Specify Application Binary Interface (possible values: \"armeabi-v7a\", \"arm64-v8a\", \"x86\", \"x86_64\"):" "x86")
|
||||
if validate_android_abi "$android_abi"; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
until [ "$?" -ne 0 ]; do
|
||||
android_version=$(request_answer "Specify Android version:" "8.1")
|
||||
if validate_android_version "$android_version" "$android_image_type" "$android_abi"; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
IFS=';' read -ra emulator_image_info <<< "$emulator_image"
|
||||
emulator_image_type=${emulator_image_info[2]}
|
||||
sed -i.bak "s|@AVD_NAME@|$avd_name|g" "$TMP_DIR/entrypoint.sh"
|
||||
sed -i.bak "s|@PLATFORM@|$platform|g" "$TMP_DIR/entrypoint.sh"
|
||||
|
||||
android_device=$(request_answer "Specify device preset name if needed (e.g. \"Nexus 4\"):")
|
||||
sdcard_size=$(request_answer "Specify SD card size, Mb:" 500)
|
||||
userdata_size=$(request_answer "Specify userdata.img size, Mb:" 500)
|
||||
|
||||
image_name="android"
|
||||
default_tag="$android_version"
|
||||
chrome_mobile=$(request_answer "Are you building a Chrome Mobile image (for mobile web testing):" "n")
|
||||
if [ "y" == "$chrome_mobile" ]; then
|
||||
sed -i.bak 's|@CHROME_MOBILE@|yes|g' "$TMP_DIR/entrypoint.sh"
|
||||
image_name="chrome-mobile"
|
||||
else
|
||||
sed -i.bak 's|@CHROME_MOBILE@||g' "$TMP_DIR/entrypoint.sh"
|
||||
fi
|
||||
|
||||
chromedriver_version=$(request_answer "Specify Chromedriver version if needed (required for Chrome Mobile):")
|
||||
if [ -n "$chromedriver_version" ]; then
|
||||
chrome_major_version="$(cut -d'.' -f1 <<<${chromedriver_version})"
|
||||
chrome_minor_version="$(cut -d'.' -f2 <<<${chromedriver_version})"
|
||||
if [ -n "$chrome_major_version" ] && [ -n "$chrome_minor_version" ]; then
|
||||
default_tag="$chrome_major_version.$chrome_minor_version"
|
||||
fi
|
||||
fi
|
||||
|
||||
tag=$(request_answer "Specify image tag:" "selenoid/$image_name:$default_tag")
|
||||
need_quickboot=$(request_answer "Add Android quick boot snapshot?" "y")
|
||||
|
||||
if [ -n "$chromedriver_version" ]; then
|
||||
download_chromedriver "$chromedriver_version"
|
||||
fi
|
||||
|
||||
rm -Rf *.bak || true
|
||||
set -x
|
||||
|
||||
tmp_tag="$tag"_tmp
|
||||
docker build -t "$tmp_tag" \
|
||||
--build-arg APPIUM_VERSION="$appium_version" \
|
||||
--build-arg ANDROID_DEVICE="$android_device" \
|
||||
--build-arg REPLACE_IMG="$replace_img" \
|
||||
--build-arg AVD_NAME="$avd_name" \
|
||||
--build-arg BUILD_TOOLS="$build_tools" \
|
||||
--build-arg PLATFORM="$platform" \
|
||||
--build-arg EMULATOR_IMAGE="$emulator_image" \
|
||||
--build-arg EMULATOR_IMAGE_TYPE="$emulator_image_type" \
|
||||
--build-arg ANDROID_ABI="$android_abi" \
|
||||
--build-arg SDCARD_SIZE="$sdcard_size" \
|
||||
--build-arg USERDATA_SIZE="$userdata_size" android
|
||||
|
||||
if [ "$need_quickboot" == "y" ]; then
|
||||
id=$(docker run -e CHROME_MOBILE="$chrome_mobile" -d --privileged "$tmp_tag")
|
||||
sleep 60
|
||||
docker exec "$id" "/usr/bin/emulator-snapshot.sh"
|
||||
sleep 30 # Wait for snapshot to save
|
||||
docker commit "$id" "$tag"
|
||||
docker rm -f "$id" || true
|
||||
else
|
||||
docker tag "$tmp_tag" "$tag"
|
||||
fi
|
||||
docker rmi -f "$tmp_tag" || true
|
||||
set +x
|
||||
|
||||
if [ "y" == "$chrome_mobile" ]; then
|
||||
test_image "$tag"
|
||||
fi
|
||||
|
||||
read -r -p "Push?" yn
|
||||
if [ "$yn" == "y" ]; then
|
||||
docker push "$tag"
|
||||
fi
|
||||
@@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
listen string
|
||||
gracePeriod time.Duration
|
||||
target string
|
||||
queue = make(chan struct{}, 1)
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.StringVar(&listen, "listen", ":4444", "host and port to listen to")
|
||||
flag.DurationVar(&gracePeriod, "grace-period", 300*time.Second, "graceful shutdown period")
|
||||
flag.StringVar(&target, "target", "http://localhost:4723", "target proxy url")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func signalContext() (context.Context, context.CancelFunc) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
go func() {
|
||||
defer cancel()
|
||||
stop := make(chan os.Signal)
|
||||
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
|
||||
<-stop
|
||||
}()
|
||||
return ctx, cancel
|
||||
}
|
||||
|
||||
func main() {
|
||||
ctx, cancel := signalContext()
|
||||
defer cancel()
|
||||
err := mainCtx(ctx)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func mux(ctx context.Context, u *url.URL) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/wd/hub/session", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
select {
|
||||
case <-r.Context().Done():
|
||||
return
|
||||
case <-ctx.Done():
|
||||
http.Error(w, http.StatusText(http.StatusServiceUnavailable), http.StatusServiceUnavailable)
|
||||
return
|
||||
case queue <- struct{}{}:
|
||||
}
|
||||
(&httputil.ReverseProxy{
|
||||
Director: func(r *http.Request) {
|
||||
r.URL.Scheme, r.URL.Host = u.Scheme, u.Host
|
||||
},
|
||||
ModifyResponse: func(resp *http.Response) error {
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
<-queue
|
||||
}
|
||||
return nil
|
||||
},
|
||||
ErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {
|
||||
<-queue
|
||||
log.Printf("proxy to %s: %v", u.String(), err)
|
||||
http.Error(w, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
|
||||
|
||||
},
|
||||
}).ServeHTTP(w, r)
|
||||
}))
|
||||
mux.HandleFunc("/wd/hub/session/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
(&httputil.ReverseProxy{
|
||||
Director: func(r *http.Request) {
|
||||
r.URL.Scheme, r.URL.Host = u.Scheme, u.Host
|
||||
},
|
||||
}).ServeHTTP(w, r)
|
||||
fragments := strings.Split(r.URL.Path, "/")
|
||||
if r.Method == http.MethodDelete && len(fragments) == 5 {
|
||||
<-queue
|
||||
}
|
||||
}))
|
||||
return mux
|
||||
}
|
||||
|
||||
func mainCtx(ctx context.Context) error {
|
||||
target, err := url.Parse(target)
|
||||
if err != nil {
|
||||
return fmt.Errorf("parse target url: %v", err)
|
||||
}
|
||||
server := &http.Server{
|
||||
Addr: listen,
|
||||
Handler: mux(ctx, target),
|
||||
}
|
||||
e := make(chan error)
|
||||
go func() {
|
||||
e <- server.ListenAndServe()
|
||||
}()
|
||||
select {
|
||||
case err := <-e:
|
||||
return err
|
||||
case <-ctx.Done():
|
||||
}
|
||||
log.Printf("starting graceful shutdown in %v]", gracePeriod)
|
||||
shCtx, cancel := context.WithTimeout(context.Background(), gracePeriod)
|
||||
defer cancel()
|
||||
if err := server.Shutdown(shCtx); err != nil {
|
||||
return fmt.Errorf("graceful shutdown: %v", err)
|
||||
}
|
||||
log.Printf("stopped")
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
FROM golang:1.17 as go
|
||||
|
||||
COPY xseld /xseld
|
||||
|
||||
COPY fileserver /fileserver
|
||||
|
||||
RUN \
|
||||
apt-get update && \
|
||||
apt-get install -y upx-ucl libx11-dev && \
|
||||
cd /xseld && \
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||
upx /xseld/xseld && \
|
||||
cd /fileserver && \
|
||||
go test -race && \
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||
upx /fileserver/fileserver
|
||||
|
||||
FROM ubuntu:20.04
|
||||
|
||||
RUN \
|
||||
apt update && \
|
||||
apt remove -y libcurl4 && \
|
||||
apt install -y apt-transport-https ca-certificates tzdata locales libcurl4 curl gnupg && \
|
||||
DEBIAN_FRONTEND=noninteractive apt -y upgrade && \
|
||||
echo ttf-mscorefonts-installer msttcorefonts/accepted-mscorefonts-eula select true | debconf-set-selections && \
|
||||
echo 'UTC' | tee /etc/timezone && \
|
||||
dpkg-reconfigure -f noninteractive tzdata && \
|
||||
echo "gtk-cursor-blink=0" > /root/.gtkrc-2.0 && \
|
||||
apt update && \
|
||||
apt install -y ttf-mscorefonts-installer \
|
||||
ttf-dejavu-core \
|
||||
fontconfig \
|
||||
fontconfig-config \
|
||||
fonts-dejavu-core \
|
||||
fonts-liberation \
|
||||
fonts-ubuntu-font-family-console \
|
||||
fonts-wqy-zenhei \
|
||||
fonts-thai-tlwg-ttf \
|
||||
fonts-ipafont-mincho \
|
||||
fonts-sahadeva \
|
||||
fonts-noto-unhinted \
|
||||
fonts-noto-color-emoji \
|
||||
libfontconfig1 \
|
||||
libfontenc1 \
|
||||
libfreetype6 \
|
||||
libxfont2 \
|
||||
libxft2 \
|
||||
libnss3-tools \
|
||||
xfonts-base \
|
||||
xfonts-encodings \
|
||||
xfonts-utils \
|
||||
flashplugin-installer \
|
||||
xvfb \
|
||||
pulseaudio \
|
||||
fluxbox \
|
||||
x11vnc \
|
||||
feh \
|
||||
wmctrl \
|
||||
libnss-wrapper \
|
||||
xsel && \
|
||||
mkdir -p /var/lib/locales/supported.d/ && grep UTF-8 /usr/share/i18n/SUPPORTED > /var/lib/locales/supported.d/all && \
|
||||
locale-gen && update-locale && \
|
||||
fc-cache -f -v && \
|
||||
adduser --system --home /home/selenium --uid 4096 \
|
||||
--ingroup root --disabled-password --shell /bin/bash selenium && \
|
||||
mkdir -p /home/selenium/Downloads && \
|
||||
mkdir -p /home/selenium/.fluxbox && \
|
||||
chgrp -R 0 /home/selenium && \
|
||||
chmod -R g=u /home/selenium && \
|
||||
ln -sf /bin/true /usr/bin/xdg-open && \
|
||||
apt-get clean && \
|
||||
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||
|
||||
COPY fluxbox /usr/share/fluxbox/styles/
|
||||
COPY --chown=selenium:root fluxbox /home/selenium/.fluxbox/
|
||||
COPY aerokube.png /usr/share/images/fluxbox/
|
||||
|
||||
COPY --from=go /fileserver/fileserver /usr/bin/
|
||||
COPY --from=go /xseld/xseld /usr/bin/
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 125 KiB |
@@ -0,0 +1,5 @@
|
||||
module fileserver
|
||||
|
||||
go 1.17
|
||||
|
||||
require github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e
|
||||
@@ -0,0 +1,2 @@
|
||||
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e h1:ogUKYFNcdYUIBSLibE4+EjbTJazoHr5JsWWx21Lpn8c=
|
||||
github.com/aandryashin/matchers v0.0.0-20161126170413-435295ea180e/go.mod h1:cbmYNkm9xeQlNoWEPtOUcvNok2gSD7ErMnYkRW+eHi8=
|
||||
@@ -0,0 +1,84 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dir, err := downloadsDir()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Fatal(http.ListenAndServe(":8080", mux(dir)))
|
||||
}
|
||||
|
||||
func downloadsDir() (string, error) {
|
||||
homeDir := os.Getenv("HOME")
|
||||
if homeDir == "" {
|
||||
homeDir = "/home/selenium"
|
||||
}
|
||||
dir := filepath.Join(homeDir, "Downloads")
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create downloads dir: %v", err)
|
||||
}
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
const jsonParam = "json"
|
||||
|
||||
func mux(dir string) http.Handler {
|
||||
mux := http.NewServeMux()
|
||||
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method == http.MethodDelete {
|
||||
deleteFileIfExists(w, r, dir)
|
||||
return
|
||||
}
|
||||
if _, ok := r.URL.Query()[jsonParam]; ok {
|
||||
listFilesAsJson(w, dir)
|
||||
return
|
||||
}
|
||||
http.FileServer(http.Dir(dir)).ServeHTTP(w, r)
|
||||
})
|
||||
return mux
|
||||
}
|
||||
|
||||
func listFilesAsJson(w http.ResponseWriter, dir string) {
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
sort.Slice(files, func(i, j int) bool {
|
||||
return files[i].ModTime().After(files[j].ModTime())
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
ret := []string{}
|
||||
for _, f := range files {
|
||||
ret = append(ret, f.Name())
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(ret)
|
||||
}
|
||||
|
||||
func deleteFileIfExists(w http.ResponseWriter, r *http.Request, dir string) {
|
||||
fileName := strings.TrimPrefix(r.URL.Path, "/")
|
||||
filePath := filepath.Join(dir, fileName)
|
||||
_, err := os.Stat(filePath)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Unknown file %s", fileName), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
err = os.Remove(filePath)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to delete file %s: %v", fileName, err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
. "github.com/aandryashin/matchers"
|
||||
. "github.com/aandryashin/matchers/httpresp"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
dir string
|
||||
srv *httptest.Server
|
||||
)
|
||||
|
||||
func init() {
|
||||
dir, _ = ioutil.TempDir("", "fileserver")
|
||||
srv = httptest.NewServer(mux(dir))
|
||||
}
|
||||
|
||||
func withUrl(path string) string {
|
||||
return srv.URL + path
|
||||
}
|
||||
|
||||
func TestDownloadAndRemoveFile(t *testing.T) {
|
||||
tempFile, _ := ioutil.TempFile(dir, "fileserver")
|
||||
ioutil.WriteFile(tempFile.Name(), []byte("test-data"), 0644)
|
||||
tempFileName := filepath.Base(tempFile.Name())
|
||||
resp, err := http.Get(withUrl("/" + tempFileName))
|
||||
AssertThat(t, err, Is{nil})
|
||||
AssertThat(t, resp, Code{200})
|
||||
_, err = os.Stat(tempFile.Name())
|
||||
AssertThat(t, err, Is{nil})
|
||||
|
||||
rsp, err := http.Get(withUrl("/?json"))
|
||||
AssertThat(t, err, Is{nil})
|
||||
AssertThat(t, rsp, Code{http.StatusOK})
|
||||
var files []string
|
||||
AssertThat(t, rsp, IsJson{&files})
|
||||
AssertThat(t, files, EqualTo{[]string{tempFileName}})
|
||||
|
||||
req, _ := http.NewRequest(http.MethodDelete, withUrl("/" + tempFileName), nil)
|
||||
resp, err = http.DefaultClient.Do(req)
|
||||
AssertThat(t, err, Is{nil})
|
||||
AssertThat(t, resp, Code{200})
|
||||
_, err = os.Stat(tempFile.Name())
|
||||
AssertThat(t, err, Not{nil})
|
||||
}
|
||||
|
||||
func TestRemoveMissingFile(t *testing.T) {
|
||||
req, _ := http.NewRequest(http.MethodDelete, withUrl("/missing-file"), nil)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
AssertThat(t, err, Is{nil})
|
||||
AssertThat(t, resp, Code{404})
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
###############################################################################
|
||||
#
|
||||
# name: aerokube (based on ubuntu_light)
|
||||
# made: paultag
|
||||
# date: 16-10-2010
|
||||
# http://pault.ag/
|
||||
#
|
||||
############################################################## BACKGROUND ######
|
||||
|
||||
# background: flat
|
||||
# background.color: #2c001e
|
||||
# background.colorTo: #2c001e
|
||||
|
||||
background: fullscreen
|
||||
background.pixmap: /usr/share/images/fluxbox/aerokube.png
|
||||
|
||||
############################################################## FONTS ##########
|
||||
|
||||
|
||||
menu.frame.font: Ubuntu-10:bold
|
||||
menu.title.font: Ubuntu-12:bold
|
||||
toolbar.clock.font: Ubuntu-10:bold
|
||||
toolbar.workspace.font: Ubuntu-12:bold
|
||||
toolbar.iconbar.focused.font: Ubuntu-10:bold
|
||||
toolbar.iconbar.unfocused.font: Ubuntu-10
|
||||
window.font: Ubuntu-10
|
||||
|
||||
############################################################## MENU ###########
|
||||
|
||||
menu.bevelWidth: 1
|
||||
|
||||
#menu.itemHeight: 35
|
||||
#menu.titleHeight: 21
|
||||
menu.borderColor: #525252
|
||||
menu.borderWidth: 1
|
||||
|
||||
menu.bullet: Triangle
|
||||
menu.bullet.position: Right
|
||||
menu.frame.underlineColor: #ffffff
|
||||
|
||||
menu.title: flat gradient rectangle
|
||||
menu.title.justify: center
|
||||
menu.title.color: #FFFFFF
|
||||
menu.title.colorTo: #FFFFFF
|
||||
menu.title.textColor: #333333
|
||||
|
||||
menu.frame: flat gradient crossdiagonal
|
||||
menu.frame.justify: left
|
||||
menu.frame.color: #FFFFFF
|
||||
menu.frame.colorTo: #FFFFFF
|
||||
menu.frame.textColor: #333333
|
||||
menu.frame.disableColor: #aea79f
|
||||
|
||||
menu.hilite: flat gradient rectangle
|
||||
menu.hilite.color: #aea79f
|
||||
menu.hilite.colorTo: #aea79f
|
||||
menu.hilite.textColor: #000000
|
||||
|
||||
############################################################## TOOLBAR ########
|
||||
|
||||
toolbar.bevelWidth: 0
|
||||
|
||||
toolbar.borderWidth: 1
|
||||
toolbar.borderColor: #aea79f
|
||||
|
||||
toolbar.height: 20
|
||||
|
||||
toolbar.justify: center
|
||||
|
||||
toolbar: flat gradient rectangle
|
||||
toolbar.pixmap:
|
||||
toolbar.color: #aea79f
|
||||
toolbar.colorTo: #aea79f
|
||||
|
||||
toolbar.clock: parentrelative
|
||||
toolbar.clock.justify: center
|
||||
toolbar.clock.color: #
|
||||
toolbar.clock.colorTo: #
|
||||
toolbar.clock.textColor: #333333
|
||||
|
||||
toolbar.workspace: parentrelative
|
||||
toolbar.workspace.justify: Center
|
||||
toolbar.workspace.color: #
|
||||
toolbar.workspace.colorTo: #
|
||||
toolbar.workspace.textColor: #333333
|
||||
|
||||
toolbar.button: parentrelative
|
||||
toolbar.button.color: #333333
|
||||
toolbar.button.colorTo: #333333
|
||||
toolbar.button.picColor: #333333
|
||||
toolbar.button.pressed: parentrelative
|
||||
toolbar.button.pressed.color: #aea79f
|
||||
toolbar.button.pressed.colorTo: #aea79f
|
||||
toolbar.button.pressed.picColor: #000000
|
||||
|
||||
toolbar.iconbar.borderWidth: 1
|
||||
toolbar.iconbar.borderColor: #333333
|
||||
|
||||
toolbar.iconbar.empty: parentrelative
|
||||
toolbar.iconbar.empty.color: #
|
||||
toolbar.iconbar.empty.colorTo: #
|
||||
|
||||
toolbar.iconbar.focused.borderWidth: 1
|
||||
toolbar.iconbar.focused.borderColor: #333333
|
||||
toolbar.iconbar.focused: flat gradient rectangle
|
||||
toolbar.iconbar.focused.color: #FFFFFF
|
||||
toolbar.iconbar.focused.colorTo: #FFFFFF
|
||||
toolbar.iconbar.focused.textColor: #333333
|
||||
toolbar.iconbar.focused.justify: center
|
||||
|
||||
toolbar.iconbar.unfocused.borderWidth: 1
|
||||
toolbar.iconbar.unfocused.borderColor: #525252
|
||||
toolbar.iconbar.unfocused: flat gradient rectangle
|
||||
toolbar.iconbar.unfocused.color: #aea79f
|
||||
toolbar.iconbar.unfocused.colorTo: #aea79f
|
||||
toolbar.iconbar.unfocused.textColor: #444444
|
||||
toolbar.iconbar.unfocused.justify: center
|
||||
|
||||
############################################################## WINDOW #########
|
||||
|
||||
window.roundCorners: TopRight TopLeft
|
||||
window.bevelWidth: 4
|
||||
window.shade: false
|
||||
window.borderWidth: 1
|
||||
window.borderColor: #333333
|
||||
|
||||
window.justify: Center
|
||||
window.title.height: 21
|
||||
|
||||
window.title.focus: flat gradient rectangle
|
||||
window.title.focus.color: #000000
|
||||
window.title.focus.colorTo: #333333
|
||||
window.title.unfocus: flat gradient rectangle
|
||||
window.title.unfocus.color: #111111
|
||||
window.title.unfocus.colorTo: #444444
|
||||
|
||||
window.label.focus: parentrelative
|
||||
window.label.focus.color: #
|
||||
window.label.focus.colorTo: #
|
||||
window.label.focus.textColor: #FFFFFF
|
||||
window.label.unfocus: parentrelative
|
||||
window.label.unfocus.color: #
|
||||
window.label.unfocus.colorTo: #
|
||||
window.label.unfocus.textColor: #aea79f
|
||||
|
||||
window.button.focus: parentrelative
|
||||
window.button.focus.color: #
|
||||
window.button.focus.colorTo: #
|
||||
window.button.focus.picColor: #FFFFFF
|
||||
window.button.unfocus: parentrelative
|
||||
window.button.unfocus.Color: #
|
||||
window.button.unfocus.ColorTo: #
|
||||
window.button.unfocus.picColor: #888888
|
||||
window.button.pressed: parentrelative
|
||||
window.button.pressed.color: #
|
||||
window.button.pressed.colorTo: #
|
||||
window.button.pressed.picColor: #000000
|
||||
|
||||
window.handle.focus: flat
|
||||
window.handle.focus.color: #1f1f1f
|
||||
window.handle.focus.colorTo: #1f1f1f
|
||||
window.handle.unfocus: lat
|
||||
window.handle.unfocus.color: #1f1f1f
|
||||
window.handle.unfocus.colorTo: #1f1f1f
|
||||
window.handleWidth: 2
|
||||
|
||||
window.grip.focus: flat
|
||||
window.grip.focus.color: #525252
|
||||
window.grip.focus.colorTo: #525252
|
||||
window.grip.unfocus: flat
|
||||
window.grip.unfocus.color: #1f1f1f
|
||||
window.grip.unfocus.colorTo: #1f1f1f
|
||||
|
||||
###############################################################################
|
||||
# EOF
|
||||
@@ -0,0 +1,7 @@
|
||||
session.menuFile: ~/.fluxbox/menu
|
||||
session.keyFile: ~/.fluxbox/keys
|
||||
session.styleFile: /usr/share/fluxbox/styles/aerokube
|
||||
session.configVersion: 13
|
||||
session.screen0.strftimeFormat: %d %b, %a %02k:%M:%S
|
||||
session.screen0.toolbar.visible: false
|
||||
session.screen0.toolbar.tools:
|
||||
@@ -0,0 +1,3 @@
|
||||
module xseld
|
||||
|
||||
go 1.17
|
||||
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
cmd := exec.Command("xsel", "-b")
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
cmd.Args = append(cmd.Args, "-o")
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
go io.Copy(w, stdout)
|
||||
case http.MethodPost:
|
||||
cmd.Args = append(cmd.Args, "-i")
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
defer stdin.Close()
|
||||
io.Copy(stdin, r.Body)
|
||||
}()
|
||||
default:
|
||||
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
log.Fatal(http.ListenAndServe(":9090", nil))
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
FROM golang:1.17 as go
|
||||
|
||||
COPY choose /choose
|
||||
|
||||
COPY rproxy /rproxy
|
||||
|
||||
RUN \
|
||||
apt-get update && \
|
||||
apt-get install -y upx-ucl && \
|
||||
cd /choose && \
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||
upx /choose/choose && \
|
||||
cd /rproxy && \
|
||||
GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" && \
|
||||
upx /rproxy/rproxy
|
||||
|
||||
FROM ubuntu:18.04
|
||||
|
||||
COPY --from=go /choose/choose /usr/local/bin/
|
||||
COPY --from=go /rproxy/rproxy /usr/local/bin/
|
||||
|
||||
RUN \
|
||||
apt update && \
|
||||
apt -y install xvfb x11-utils x11vnc tigervnc-common tigervnc-viewer
|
||||
|
||||
COPY entrypoint.sh /
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
#browsers.json
|
||||
|
||||
```
|
||||
"MicrosoftEdge": {
|
||||
"default": "77",
|
||||
"versions": {
|
||||
"77": {
|
||||
"image": "selenoid/external-host:1.0.0",
|
||||
"port": "4444",
|
||||
"path": "/",
|
||||
"env": ["URLS=[\"http://172.17.0.1:4444/\"]","VNC_PASSWORD=longpassword","SCREEN_RESOLUTION=1921x1080x24"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
"fmt"
|
||||
"encoding/json"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
var (
|
||||
s rand.Source
|
||||
r *rand.Rand
|
||||
)
|
||||
|
||||
func init() {
|
||||
s = rand.NewSource(time.Now().Unix())
|
||||
r = rand.New(s)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var hosts []string
|
||||
if err := json.Unmarshal([]byte(os.Getenv("URLS")), &hosts); err != nil {
|
||||
fmt.Printf("unmarshal json: %v", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if l := len(hosts); l > 0 {
|
||||
fmt.Println(hosts[r.Intn(l)])
|
||||
}
|
||||
}
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
SCREEN_RESOLUTION=${SCREEN_RESOLUTION:-"1920x1080x24"}
|
||||
DISPLAY_NUM=99
|
||||
URLS=${URLS:-'["http://172.17.0.1:4444/"]'}
|
||||
VNC_PASSWORD=${VNC_PASSWORD:-"password"}
|
||||
export URLS
|
||||
export VNC_PASSWORD
|
||||
export DISPLAY=":$DISPLAY_NUM"
|
||||
|
||||
clean() {
|
||||
if [ -n "$XVFB_PID" ]; then
|
||||
kill -TERM "$XVFB_PID"
|
||||
fi
|
||||
if [ -n "$X11VNC_PID" ]; then
|
||||
kill -TERM "$X11VNC_PID"
|
||||
fi
|
||||
if [ -n "$RPROXY_PID" ]; then
|
||||
kill -TERM "$RPROXY_PID"
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
trap clean SIGINT SIGTERM
|
||||
|
||||
URL=$(choose $URL)
|
||||
HOST=$(echo $URL | sed -e 's@\(.*\)://@@' -e 's@[:/]\(.*\)$@@')
|
||||
|
||||
/bin/bash -c 'echo -e "$VNC_PASSWORD\n$VNC_PASSWORD\nn\n" | vncpasswd file.passwd'>/dev/null
|
||||
|
||||
xvfb-run -l -n $DISPLAY_NUM -s "-ac -screen 0 $SCREEN_RESOLUTION -noreset -listen tcp" \
|
||||
vncviewer passwd=file.passwd $HOST &
|
||||
XVFB_PID=$!
|
||||
|
||||
retcode=1
|
||||
until [ $retcode -eq 0 ]; do
|
||||
xdpyinfo -display $DISPLAY >/dev/null 2>&1
|
||||
retcode=$?
|
||||
if [ $retcode -ne 0 ]; then
|
||||
echo Waiting xvfb...
|
||||
sleep 1
|
||||
fi
|
||||
done
|
||||
|
||||
x11vnc -display $DISPLAY -passwd selenoid -shared -forever -loop500 -rfbport 5900 -rfbportv6 5900 -logfile /dev/null &
|
||||
X11VNC_PID=$!
|
||||
|
||||
rproxy $URL &
|
||||
RPROXY_PID=$!
|
||||
|
||||
wait
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
u, err := url.Parse(os.Args[1])
|
||||
if err != nil {
|
||||
log.Fatalf("parse url: %v", err)
|
||||
}
|
||||
log.Fatal(http.ListenAndServe(":4444", httputil.NewSingleHostReverseProxy(u)))
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
FROM browsers/base:7.3.6
|
||||
|
||||
RUN \
|
||||
curl -s https://deb.opera.com/archive.key | apt-key add - && \
|
||||
echo 'deb https://deb.opera.com/opera/ stable non-free' >> /etc/apt/sources.list.d/opera.list && \
|
||||
apt-get update && \
|
||||
apt-get -y install opera=12.16.1860 openjdk-8-jre-headless && \
|
||||
rm -Rf /tmp/* && rm -Rf /var/lib/apt/lists/*
|
||||
|
||||
COPY selenium-server-standalone.jar /usr/share/selenium/
|
||||
COPY entrypoint.sh /
|
||||
|
||||
USER selenium
|
||||
|
||||
EXPOSE 4444
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
Executable
+54
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
SCREEN_RESOLUTION=${SCREEN_RESOLUTION:-"1920x1080x24"}
|
||||
ENABLE_WINDOW_MANAGER=${ENABLE_WINDOW_MANAGER:-""}
|
||||
DISPLAY_NUM=99
|
||||
export DISPLAY=":$DISPLAY_NUM"
|
||||
|
||||
clean() {
|
||||
if [ -n "$FILESERVER_PID" ]; then
|
||||
kill -TERM "$FILESERVER_PID"
|
||||
fi
|
||||
if [ -n "$XSELD_PID" ]; then
|
||||
kill -TERM "$XSELD_PID"
|
||||
fi
|
||||
if [ -n "$XVFB_PID" ]; then
|
||||
kill -TERM "$XVFB_PID"
|
||||
fi
|
||||
if [ -n "$SELENIUM_PID" ]; then
|
||||
kill -TERM "$SELENIUM_PID"
|
||||
fi
|
||||
if [ -n "$X11VNC_PID" ]; then
|
||||
kill -TERM "$X11VNC_PID"
|
||||
fi
|
||||
}
|
||||
|
||||
trap clean SIGINT SIGTERM
|
||||
|
||||
/usr/bin/fileserver &
|
||||
FILESERVER_PID=$!
|
||||
|
||||
DISPLAY="$DISPLAY" /usr/bin/xseld &
|
||||
XSELD_PID=$!
|
||||
|
||||
/usr/bin/xvfb-run -l -n "$DISPLAY_NUM" -s "-ac -screen 0 $SCREEN_RESOLUTION -noreset -listen tcp" /usr/bin/fluxbox -display "$DISPLAY" -log /tmp/fluxbox.log 2>/dev/null &
|
||||
XVFB_PID=$!
|
||||
|
||||
retcode=1
|
||||
until [ $retcode -eq 0 ]; do
|
||||
DISPLAY="$DISPLAY" wmctrl -m >/dev/null 2>&1
|
||||
retcode=$?
|
||||
if [ $retcode -ne 0 ]; then
|
||||
echo Waiting X server...
|
||||
sleep 0.1
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$ENABLE_VNC" == "true" ]; then
|
||||
x11vnc -display "$DISPLAY" -passwd selenoid -shared -forever -loop500 -rfbport 5900 -rfbportv6 5900 -logfile /tmp/x11vnc.log &
|
||||
X11VNC_PID=$!
|
||||
fi
|
||||
|
||||
/usr/bin/java -Xmx256m -Djava.security.egd=file:/dev/./urandom -jar /usr/share/selenium/selenium-server-standalone.jar -port 4444 -browserTimeout 120 &
|
||||
SELENIUM_PID=$!
|
||||
|
||||
wait
|
||||
@@ -0,0 +1,3 @@
|
||||
FROM wernight/phantomjs:2.1.1
|
||||
EXPOSE 4444
|
||||
CMD ["/usr/local/bin/phantomjs", "--webdriver=4444"]
|
||||
@@ -0,0 +1,10 @@
|
||||
# Rebuilding Debian Packages
|
||||
|
||||
1. Download package from APT-repository:
|
||||
```
|
||||
$ apt-get download google-chrome-stable=48.0.2564.109-1+suffix0
|
||||
```
|
||||
2. Rebuild package to new owner using script:
|
||||
```
|
||||
$ rebuild-deb.sh -f google-chrome-stable=48.0.2564.109-1+suffix0_amd64.deb -r 'suffix0' -a 'aerokube0'
|
||||
```
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
usage()
|
||||
{
|
||||
cat << EOF
|
||||
usage: $0 options
|
||||
|
||||
OPTIONS:
|
||||
-h Show this message
|
||||
-f Filename
|
||||
-r Remove version postfix
|
||||
-a Add version postfix
|
||||
EOF
|
||||
}
|
||||
|
||||
filename=''
|
||||
remove_postfix=''
|
||||
add_postfix=''
|
||||
|
||||
while getopts 'f:r:a:' OPTION
|
||||
do
|
||||
case $OPTION in
|
||||
h)
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
f)
|
||||
filename=$OPTARG
|
||||
;;
|
||||
r)
|
||||
remove_postfix=$OPTARG
|
||||
;;
|
||||
a)
|
||||
add_postfix=$OPTARG
|
||||
;;
|
||||
?)
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$filename" ]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
set -x
|
||||
|
||||
cp "$filename" .
|
||||
spackage=$(echo "$filename" | awk -F '/' '{print $NF}')
|
||||
package=$(echo "$spackage" | awk -F '_' '{print $1}')
|
||||
version=$(echo "$spackage" | awk -F '_' '{print $2}')
|
||||
rm -Rf "$package" control changelog
|
||||
|
||||
# Set variables
|
||||
export DH_ALWAYS_EXCLUDE="CVS:.svn:.git"
|
||||
|
||||
# Extract package content
|
||||
dpkg-deb -x $spackage $package/
|
||||
|
||||
# Extract package control
|
||||
dpkg-deb -e $spackage $package/DEBIAN
|
||||
|
||||
# Extract package changelog
|
||||
changelog=$(find $package/usr/share/doc -name 'changelog*.gz' 2>/dev/null| head -n 1)
|
||||
if [ -n "$changelog" -a -f "$changelog" ]; then
|
||||
rm -f "$changelog"
|
||||
fi
|
||||
old_version=$version
|
||||
if [ -n "$remove_postfix" ]; then
|
||||
version=$(echo $version | sed -e "s|+$remove_postfix||g")
|
||||
old_version=$version"+"$remove_postfix
|
||||
fi
|
||||
new_version=$version
|
||||
if [ -n "$add_postfix" ]; then
|
||||
new_version=$new_version"+"$add_postfix
|
||||
fi
|
||||
dch --create --force-distribution --distribution unstable --package "$package" --newversion "$new_version" -c changelog 'Package rebuilt'
|
||||
|
||||
# Fix control
|
||||
section=$(awk '/Section:/ {print $NF}' $package/DEBIAN/control 2>/dev/null || echo 'misc')
|
||||
priority=$(awk '/Priority:/ {print $NF}' $package/DEBIAN/control 2>/dev/null || echo 'extra')
|
||||
echo "$(echo $spackage | sed "s/$old_version/$new_version/") $section $priority" > files
|
||||
echo "Source: $package" > control
|
||||
grep -iv "Source:" $package/DEBIAN/control >> control
|
||||
cp control $package/DEBIAN/control
|
||||
|
||||
# Build package
|
||||
sed -i "s/$old_version/$new_version/g" $package/DEBIAN/control
|
||||
dpkg-deb -b $package/ ./
|
||||
|
||||
# Remove source package
|
||||
rm -f "./$spackage"
|
||||
@@ -0,0 +1,76 @@
|
||||
# Contributor: Anton Dzyk <anton.dzyk@yandex.ru>
|
||||
# Maintainer: Ivan Krutov <vania-pooh@aerokube.com>
|
||||
pkgname=ffmpeg-x11grab
|
||||
pkgsource=ffmpeg
|
||||
pkgver=4.3
|
||||
pkgrel=1
|
||||
pkgdesc="FFMPEG version optimized for x11grab to x264"
|
||||
url="https://ffmpeg.org"
|
||||
arch="all"
|
||||
license="LGPL-2.1-or-later GPL-2.0-or-later"
|
||||
makedepends="gnutls-dev lame-dev libvorbis-dev xvidcore-dev zlib-dev libvdpau-dev
|
||||
imlib2-dev x264-dev libtheora-dev coreutils bzip2-dev perl-dev libvpx-dev
|
||||
libvpx-dev sdl2-dev libxfixes-dev libva-dev v4l-utils-dev yasm opus-dev libass-dev pulseaudio-dev"
|
||||
source="https://ffmpeg.org/releases/ffmpeg-$pkgver.tar.xz
|
||||
0001-libavutil-clean-up-unused-FF_SYMVER-macro.patch
|
||||
"
|
||||
builddir="$srcdir/$pkgsource-$pkgver"
|
||||
|
||||
build() {
|
||||
local _dbg="--disable-debug"
|
||||
local _asm=""
|
||||
[ -n "$DEBUG" ] && _dbg="--enable-debug"
|
||||
|
||||
case "$CARCH" in
|
||||
x86 | arm*) _asm="--disable-asm" ;;
|
||||
esac
|
||||
|
||||
cd "$builddir"
|
||||
./configure \
|
||||
--prefix=/usr \
|
||||
--enable-avfilter \
|
||||
--enable-gpl \
|
||||
--enable-libx264 \
|
||||
--enable-libpulse \
|
||||
--enable-static \
|
||||
--enable-small \
|
||||
--disable-ffplay \
|
||||
--disable-ffprobe \
|
||||
--disable-doc \
|
||||
--disable-htmlpages \
|
||||
--disable-manpages \
|
||||
--disable-podpages \
|
||||
--disable-txtpages \
|
||||
--disable-w32threads \
|
||||
--disable-alsa \
|
||||
--disable-audiotoolbox \
|
||||
--disable-cuda \
|
||||
--disable-cuvid \
|
||||
--disable-d3d11va \
|
||||
--disable-dxva2 \
|
||||
--disable-nvenc \
|
||||
--disable-vaapi \
|
||||
--disable-vdpau \
|
||||
--disable-videotoolbox \
|
||||
--disable-librtmp \
|
||||
--disable-devices \
|
||||
--enable-indev=xcbgrab \
|
||||
--enable-indev=pulse \
|
||||
$_asm $_dbg
|
||||
make
|
||||
}
|
||||
|
||||
package() {
|
||||
cd "$builddir"
|
||||
make DESTDIR="$pkgdir" install
|
||||
}
|
||||
|
||||
libs() {
|
||||
pkgdesc="Libraries for ffmpeg"
|
||||
replaces="ffmpeg"
|
||||
mkdir -p "$subpkgdir"/usr
|
||||
mv "$pkgdir"/usr/lib "$subpkgdir"/usr
|
||||
}
|
||||
|
||||
sha512sums="f031eb6c4423887af323ab7d1f431234d4e30993a52db45dccf427b41eb442a3bd020dcbc13e83cbf813fad0f36c849cb651203570148387c864507aa19f313a ffmpeg-4.3.tar.xz
|
||||
1047a23eda51b576ac200d5106a1cd318d1d5291643b3a69e025c0a7b6f3dbc9f6eb0e1e6faa231b7e38c8dd4e49a54f7431f87a93664da35825cc2e9e8aedf4 0001-libavutil-clean-up-unused-FF_SYMVER-macro.patch"
|
||||
@@ -0,0 +1,21 @@
|
||||
FROM alpine:3.12
|
||||
|
||||
RUN apk add -U alpine-sdk && adduser -g "" -D -G abuild selenoid
|
||||
USER selenoid
|
||||
RUN git clone git://git.alpinelinux.org/aports /home/selenoid/aports
|
||||
ADD APKBUILD /home/selenoid/aports/community/ffmpeg/
|
||||
RUN cd /home/selenoid/aports/community/ffmpeg/ && \
|
||||
abuild checksum && \
|
||||
abuild unpack && \
|
||||
abuild deps && \
|
||||
abuild build
|
||||
USER root
|
||||
|
||||
FROM alpine:3.12
|
||||
|
||||
RUN apk add -U x264-libs sdl2 libxcb libbz2 xset pulseaudio-utils && \
|
||||
rm -rf /var/cache/apk/*
|
||||
COPY --from=0 /home/selenoid/aports/community/ffmpeg/src/ffmpeg-4.3/ffmpeg /usr/bin/ffmpeg
|
||||
|
||||
COPY entrypoint.sh /
|
||||
ENTRYPOINT ["/entrypoint.sh"]
|
||||
Executable
+42
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
VIDEO_SIZE=${VIDEO_SIZE:-"1920x1080"}
|
||||
BROWSER_CONTAINER_NAME=${BROWSER_CONTAINER_NAME:-"browser"}
|
||||
DISPLAY=${DISPLAY:-"99"}
|
||||
FILE_NAME=${FILE_NAME:-"video.mp4"}
|
||||
FRAME_RATE=${FRAME_RATE:-"12"}
|
||||
CODEC=${CODEC:-"libx264"}
|
||||
PRESET=${PRESET:-""}
|
||||
if [ "$CODEC" == "libx264" -a -n "$PRESET" ]; then
|
||||
PRESET="-preset $PRESET"
|
||||
fi
|
||||
INPUT_OPTIONS=${INPUT_OPTIONS:-""}
|
||||
HIDE_CURSOR=${HIDE_CURSOR:-""}
|
||||
if [ -n "$HIDE_CURSOR" ]; then
|
||||
INPUT_OPTIONS="$INPUT_OPTIONS -draw_mouse 0"
|
||||
fi
|
||||
|
||||
retcode=1
|
||||
max_attempts=300
|
||||
attempts=0
|
||||
echo 'Waiting for display to open...'
|
||||
until [ $retcode -eq 0 -o $attempts -eq $max_attempts ]; do
|
||||
xset -display ${BROWSER_CONTAINER_NAME}:${DISPLAY} b off > /dev/null 2>&1
|
||||
retcode=$?
|
||||
if [ $retcode -ne 0 ]; then
|
||||
echo 'Sleeping before next attempt...'
|
||||
sleep 0.1
|
||||
fi
|
||||
attempts=$((attempts+1))
|
||||
done
|
||||
|
||||
mkdir -p ~/.config/pulse
|
||||
|
||||
echo -n 'gIvST5iz2S0J1+JlXC1lD3HWvg61vDTV1xbmiGxZnjB6E3psXsjWUVQS4SRrch6rygQgtpw7qmghDFTaekt8qWiCjGvB0LNzQbvhfs1SFYDMakmIXuoqYoWFqTJ+GOXYByxpgCMylMKwpOoANEDePUCj36nwGaJNTNSjL8WBv+Bf3rJXqWnJ/43a0hUhmBBt28Dhiz6Yqowa83Y4iDRNJbxih6rB1vRNDKqRr/J9XJV+dOlM0dI+K6Vf5Ag+2LGZ3rc5sPVqgHgKK0mcNcsn+yCmO+XLQHD1K+QgL8RITs7nNeF1ikYPVgEYnc0CGzHTMvFR7JLgwL2gTXulCdwPbg=='| base64 -d>~/.config/pulse/cookie
|
||||
|
||||
export PULSE_SERVER=${BROWSER_CONTAINER_NAME}
|
||||
|
||||
if pactl info >/dev/null 2>&1; then
|
||||
exec ffmpeg -f pulse -i default -y -f x11grab -video_size ${VIDEO_SIZE} -r ${FRAME_RATE} ${INPUT_OPTIONS} -i ${BROWSER_CONTAINER_NAME}:${DISPLAY} -codec:v ${CODEC} ${PRESET} -pix_fmt yuv420p -filter:v "pad=ceil(iw/2)*2:ceil(ih/2)*2" "/data/$FILE_NAME"
|
||||
else
|
||||
exec ffmpeg -y -f x11grab -video_size ${VIDEO_SIZE} -r ${FRAME_RATE} ${INPUT_OPTIONS} -i ${BROWSER_CONTAINER_NAME}:${DISPLAY} -codec:v ${CODEC} ${PRESET} -pix_fmt yuv420p -filter:v "pad=ceil(iw/2)*2:ceil(ih/2)*2" "/data/$FILE_NAME"
|
||||
fi
|
||||
Reference in New Issue
Block a user