1
0
mirror of https://github.com/pravets/oscript-images.git synced 2025-11-25 22:32:37 +02:00

Merge pull request #1 from pravets/develop

Реализована сборка oscript
This commit is contained in:
Iosif Pravets
2025-06-10 00:52:40 +03:00
committed by GitHub
8 changed files with 751 additions and 0 deletions

37
.github/workflows/build-oscript.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Build Oscript Docker Image
on:
push:
tags:
- 'oscript_*' # реагировать на теги, начинающиеся с executor_
jobs:
build:
runs-on: ubuntu-latest
env:
DOCKER_LOGIN: ${{ secrets.DOCKER_LOGIN }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.DOCKER_REGISTRY_URL }}
username: ${{ secrets.DOCKER_LOGIN }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set environment variables
run: |
echo "DOCKER_REGISTRY_URL=${{ secrets.DOCKER_REGISTRY_URL }}" >> "$GITHUB_ENV"
- name: Build and Push Docker image
run: |
export OSCRIPT_VERSION="${GITHUB_REF#refs/tags/oscript_}"
echo "Собираем oscript версии ${OSCRIPT_VERSION}"
./src/build-oscript.sh

22
scripts/cleanup.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
set -euo pipefail
# Разлогинивание из Docker
if [ -n "$DOCKER_REGISTRY_URL" ]; then
docker logout "$DOCKER_REGISTRY_URL"
else
docker logout
fi
# Очистка переменных среды из .env
if [ -f .env ]; then
while IFS='=' read -r var _; do
# Удаляем пробелы и префикс export, если есть
var=$(echo "$var" | sed -e 's/^export[[:space:]]*//')
if [[ $var != "" && $var != \#* ]]; then
unset "$var"
fi
done < .env
fi
echo "Очистка завершена."

16
scripts/docker_login.sh Executable file
View File

@@ -0,0 +1,16 @@
#!/bin/bash
# Проверка наличия необходимых переменных среды
if [[ -z "$DOCKER_REGISTRY_URL" || -z "$DOCKER_LOGIN" || -z "$DOCKER_PASSWORD" ]]; then
echo "Ошибка: Необходимо установить переменные среды DOCKER_REGISTRY_URL, DOCKER_LOGIN и DOCKER_PASSWORD."
exit 1
fi
echo "$DOCKER_PASSWORD" | docker login "$DOCKER_REGISTRY_URL" -u "$DOCKER_LOGIN" --password-stdin
if [[ $? -eq 0 ]]; then
echo "Успешная авторизация в $DOCKER_REGISTRY_URL"
else
echo "Ошибка авторизации в $DOCKER_REGISTRY_URL"
exit 1
fi

17
scripts/load_env.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/bin/bash
# Путь к .env файлу (по умолчанию в текущей директории)
ENV_FILE=".env"
# Проверяем, существует ли файл
if [ ! -f "$ENV_FILE" ]; then
echo "Файл $ENV_FILE не найден."
exit 1
fi
# Загружаем переменные окружения из .env файла
set -a
source "$ENV_FILE"
set +a
echo "Переменные окружения загружены из $ENV_FILE"

64
src/build-oscript.sh Executable file
View File

@@ -0,0 +1,64 @@
#!/bin/bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -z "${CI:-}" ]; then
echo "The script is not running in CI"
source "${SCRIPT_DIR}/../scripts/load_env.sh"
else
echo "The script is running in CI";
fi
source "${SCRIPT_DIR}/../scripts/docker_login.sh"
source "${SCRIPT_DIR}/../tools/assert.sh"
if [[ "${DOCKER_SYSTEM_PRUNE:-}" = "true" ]] ;
then
docker system prune -af
fi
last_arg="."
if [[ ${NO_CACHE:-} = "true" ]] ; then
last_arg="--no-cache ."
fi
oscript_version="${OSCRIPT_VERSION}"
docker build \
--pull \
--build-arg OSCRIPT_VERSION="${oscript_version}" \
-t "${DOCKER_REGISTRY_URL}/${DOCKER_LOGIN}/oscript:${oscript_version}" \
-f "${SCRIPT_DIR}/oscript/Dockerfile" \
${last_arg}
if ./tests/test-oscript.sh; then
container_version=$(docker run --rm "${DOCKER_REGISTRY_URL}/${DOCKER_LOGIN}/oscript:${oscript_version}" -v | head -n1 | awk '{print $NF}')
if [[ -n "${container_version}" ]]; then
docker push "${DOCKER_REGISTRY_URL}/${DOCKER_LOGIN}/oscript:${oscript_version}"
docker tag "${DOCKER_REGISTRY_URL}/${DOCKER_LOGIN}/oscript:${oscript_version}" "${DOCKER_REGISTRY_URL}/${DOCKER_LOGIN}/oscript:${container_version}"
docker push "${DOCKER_REGISTRY_URL}/${DOCKER_LOGIN}/oscript:${container_version}"
if ! [[ "${oscript_version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] && ! [[ "${container_version}" =~ rc ]]; then
semver_tag=$(echo "${container_version}" | awk -F. '{print $1"."$2"."$3}')
if [[ -n "${semver_tag}" ]]; then
docker tag "${DOCKER_REGISTRY_URL}/${DOCKER_LOGIN}/oscript:${oscript_version}" "${DOCKER_REGISTRY_URL}/${DOCKER_LOGIN}/oscript:${semver_tag}"
docker push "${DOCKER_REGISTRY_URL}/${DOCKER_LOGIN}/oscript:${semver_tag}"
else
echo "Не удалось получить корректную semver версию из контейнера"
exit 1
fi
fi
else
echo "Не удалось получить версию из контейнера"
exit 1
fi
source "${SCRIPT_DIR}/../scripts/cleanup.sh"
else
log_failure "ERROR: Tests failed. Docker image will not be pushed."
source "${SCRIPT_DIR}/../scripts/cleanup.sh"
exit 1
fi
exit 0

55
src/oscript/Dockerfile Normal file
View File

@@ -0,0 +1,55 @@
ARG DOCKER_REGISTRY_URL=library
ARG BASE_IMAGE=ubuntu
ARG BASE_TAG=24.04
FROM ${DOCKER_REGISTRY_URL}/${BASE_IMAGE}:${BASE_TAG}
LABEL maintainer="Iosif Pravets <i@pravets.ru>"
# mono and oscript dependencies
ARG MONO_VERSION=6.12.0.182
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates \
gnupg \
dirmngr \
wget \
locales \
&& apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF \
&& echo "deb http://download.mono-project.com/repo/debian stable-buster/snapshots/$MONO_VERSION main" > /etc/apt/sources.list.d/mono-official-stable.list \
&& apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
mono-runtime \
ca-certificates-mono \
libmono-i18n4.0-all \
libmono-system-runtime-serialization4.0-cil \
libicu-dev \
&& rm -rf /etc/apt/sources.list.d/mono-official-stable.list \
&& apt-get update \
&& cert-sync --user /etc/ssl/certs/ca-certificates.crt \
&& rm -rf \
/var/lib/apt/lists/* \
/var/cache/debconf \
RUN locale-gen ru_RU.UTF-8 \
&& localedef -i ru_RU -c -f UTF-8 -A /usr/share/locale/locale.alias ru_RU.UTF-8
# locale
ENV LANG=ru_RU.UTF-8
ENV LANGUAGE=ru_RU:ru
ENV LC_ALL=ru_RU.UTF-8
# oscript
ARG OVM_VERSION=1.6.1
ARG OSCRIPT_VERSION
RUN wget https://github.com/oscript-library/ovm/releases/download/v${OVM_VERSION}/ovm.exe \
&& mv ovm.exe /usr/local/bin/ \
&& echo 'mono /usr/local/bin/ovm.exe "$@"' | tee /usr/local/bin/ovm \
&& chmod +x /usr/local/bin/ovm \
&& ovm install ${OSCRIPT_VERSION} \
&& ovm use ${OSCRIPT_VERSION}
ENV OSCRIPTBIN=/root/.local/share/ovm/current/bin
ENV PATH="$OSCRIPTBIN:$PATH"
ENTRYPOINT ["oscript"]

32
tests/test-oscript.sh Executable file
View File

@@ -0,0 +1,32 @@
#!/bin/bash
set -e
if [ -z "${CI-}" ]; then
echo "The script is not running in CI"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/../.env"
else
echo "The script is running in CI"
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/../tools/assert.sh"
test_oscript_is_running() {
log_header "Test :: oscript is running"
local expected actual
expected="1Script Execution Engine"
actual=$(docker run --rm $DOCKER_REGISTRY_URL/${DOCKER_LOGIN}/oscript:$OSCRIPT_VERSION)
if assert_contain "$actual" "$expected"; then
log_success "oscript is running test passed"
else
log_failure "oscript is running test failed"
fi
}
# test calls
test_oscript_is_running

508
tools/assert.sh Normal file
View File

@@ -0,0 +1,508 @@
#!/usr/bin/env bash
#####################################################################
##
## title: Assert Extension
##
## description:
## Assert extension of shell (bash, ...)
## with the common assert functions
## Function list based on:
## http://junit.sourceforge.net/javadoc/org/junit/Assert.html
## Log methods : inspired by
## - https://natelandau.com/bash-scripting-utilities/
## author: Mark Torok
##
## date: 07. Dec. 2016
##
## license: MIT
##
#####################################################################
if command -v tput &>/dev/null && tty -s; then
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
MAGENTA=$(tput setaf 5)
NORMAL=$(tput sgr0)
BOLD=$(tput bold)
else
RED=$(echo -en "\e[31m")
GREEN=$(echo -en "\e[32m")
MAGENTA=$(echo -en "\e[35m")
NORMAL=$(echo -en "\e[00m")
BOLD=$(echo -en "\e[01m")
fi
# Выводит заголовок в терминал жирным пурпурным цветом.
#
# Arguments:
#
# * Текст заголовка для отображения.
#
# Outputs:
#
# * Печатает форматированный заголовок в STDERR.
log_header() {
printf "\n${BOLD}${MAGENTA}========== %s ==========${NORMAL}\n" "$@" >&2
}
# Выводит сообщение об успешном выполнении с зелёной галочкой в стандартный поток ошибок.
#
# Arguments:
#
# * Сообщение об успехе (строка или несколько строк)
#
# Outputs:
#
# * Сообщение с зелёной галочкой в STDERR.
#
# Example:
#
# log_success "Тест успешно пройден"
log_success() {
printf "${GREEN}✔ %s${NORMAL}\n" "$@" >&2
}
# Выводит сообщение об ошибке с красным крестиком в стандартный поток ошибок.
#
# Arguments:
#
# * Сообщение об ошибке для отображения.
#
# Outputs:
#
# * Сообщение об ошибке с цветовым выделением в STDERR.
#
# Example:
#
# ```bash
# log_failure "Тест не пройден"
# ```
log_failure() {
printf "${RED}✖ %s${NORMAL}\n" "$@" >&2
}
# Проверяет, равны ли ожидаемое и фактическое значения.
#
# Arguments:
#
# * expected — ожидаемое значение.
# * actual — фактическое значение.
# * msg (необязательно) — сообщение, выводимое при ошибке.
#
# Returns:
#
# * 0, если значения равны; 1, если не равны (и при этом выводится сообщение об ошибке, если оно указано).
#
# Example:
#
# assert_eq "foo" "$result" "Результат не совпадает с ожидаемым"
assert_eq() {
local expected="$1"
local actual="$2"
local msg="${3-}"
if [ "$expected" == "$actual" ]; then
return 0
else
[ "${#msg}" -gt 0 ] && log_failure "$expected == $actual :: $msg" || true
return 1
fi
}
# Проверяет, что значения не равны друг другу.
#
# Возвращает 0, если значения различны; иначе выводит сообщение об ошибке (если указано) и возвращает 1.
#
# Аргументы:
#
# * Ожидаемое значение
# * Фактическое значение
# * Необязательное сообщение для вывода при ошибке
#
# Пример:
#
# ```bash
# assert_not_eq "foo" "bar" "Значения совпадают"
# ```
assert_not_eq() {
local expected="$1"
local actual="$2"
local msg="${3-}"
if [ ! "$expected" == "$actual" ]; then
return 0
else
[ "${#msg}" -gt 0 ] && log_failure "$expected != $actual :: $msg" || true
return 1
fi
}
# Проверяет, равно ли значение строке "true".
#
# Arguments:
#
# * actual — проверяемое значение.
# * msg — необязательное сообщение, выводимое при ошибке.
#
# Returns:
#
# * 0, если значение равно "true"; иначе 1.
assert_true() {
local actual="$1"
local msg="${2-}"
assert_eq true "$actual" "$msg"
return "$?"
}
# Проверяет, что переданное значение равно "false".
#
# Arguments:
#
# * actual — проверяемое значение.
# * msg — необязательное сообщение, выводимое при ошибке.
#
# Returns:
#
# * 0, если значение равно "false"; иначе 1.
assert_false() {
local actual="$1"
local msg="${2-}"
assert_eq false "$actual" "$msg"
return "$?"
}
# Проверяет, что два массива равны по длине и содержимому.
#
# Arguments:
#
# * Имя массива с ожидаемыми значениями (передаётся по ссылке)
# * Имя массива с фактическими значениями (передаётся по ссылке)
# * Необязательное сообщение, выводимое при ошибке
#
# Returns:
#
# * 0, если массивы идентичны по длине и значениям; 1 в противном случае.
#
# Example:
#
# ```bash
# arr1=(a b c)
# arr2=(a b c)
# assert_array_eq arr1[@] arr2[@] "Массивы не совпадают"
# ```
assert_array_eq() {
declare -a expected=("${!1-}")
# echo "AAE ${expected[@]}"
declare -a actual=("${!2}")
# echo "AAE ${actual[@]}"
local msg="${3-}"
local return_code=0
if [ ! "${#expected[@]}" == "${#actual[@]}" ]; then
return_code=1
fi
local i
for (( i=1; i < ${#expected[@]} + 1; i+=1 )); do
if [ ! "${expected[$i-1]}" == "${actual[$i-1]}" ]; then
return_code=1
break
fi
done
if [ "$return_code" == 1 ]; then
[ "${#msg}" -gt 0 ] && log_failure "(${expected[*]}) != (${actual[*]}) :: $msg" || true
fi
return "$return_code"
}
# Проверяет, что два массива не равны по длине или содержимому.
#
# Arguments:
#
# * Имя переменной ожидаемого массива (по ссылке)
# * Имя переменной фактического массива (по ссылке)
# * Необязательное сообщение для вывода при неудаче
#
# Returns:
#
# * 0 — если массивы различаются по длине или хотя бы одному элементу
# * 1 — если массивы идентичны по длине и содержимому
#
# Example:
#
# ```bash
# arr1=(a b c)
# arr2=(a b d)
# assert_array_not_eq arr1[@] arr2[@] "Массивы не должны совпадать"
# ```
assert_array_not_eq() {
declare -a expected=("${!1-}")
declare -a actual=("${!2}")
local msg="${3-}"
local return_code=1
if [ ! "${#expected[@]}" == "${#actual[@]}" ]; then
return_code=0
fi
local i
for (( i=1; i < ${#expected[@]} + 1; i+=1 )); do
if [ ! "${expected[$i-1]}" == "${actual[$i-1]}" ]; then
return_code=0
break
fi
done
if [ "$return_code" == 1 ]; then
[ "${#msg}" -gt 0 ] && log_failure "(${expected[*]}) == (${actual[*]}) :: $msg" || true
fi
return "$return_code"
}
# Проверяет, является ли переданная строка пустой.
#
# Arguments:
#
# * Строка для проверки.
# * Необязательное сообщение, выводимое при ошибке.
#
# Returns:
#
# * 0, если строка пуста; 1 в противном случае.
#
# Example:
#
# ```bash
# assert_empty "" "Строка должна быть пустой"
# ```
assert_empty() {
local actual=$1
local msg="${2-}"
assert_eq "" "$actual" "$msg"
return "$?"
}
# Проверяет, что переданная строка не пуста.
#
# Arguments:
#
# * actual — строка для проверки.
# * msg — необязательное сообщение, выводимое при ошибке.
#
# Returns:
#
# * 0, если строка не пуста; 1, если строка пуста.
#
# Example:
#
# ```bash
# assert_not_empty "hello" "Строка не должна быть пустой"
# ```
assert_not_empty() {
local actual=$1
local msg="${2-}"
assert_not_eq "" "$actual" "$msg"
return "$?"
}
# Проверяет, содержит ли строка подстроку.
#
# Arguments:
#
# * haystack — строка, в которой выполняется поиск.
# * needle — подстрока для поиска.
# * msg — необязательное сообщение, выводимое при неудаче.
#
# Returns:
#
# * 0, если подстрока найдена или needle пустая; 1 в противном случае.
#
# Example:
#
# ```bash
# assert_contain "hello world" "world" # вернёт 0
# assert_contain "hello world" "foo" "Не найдено" # вернёт 1 и выведет сообщение об ошибке
# ```
assert_contain() {
local haystack="$1"
local needle="${2-}"
local msg="${3-}"
if [ -z "${needle:+x}" ]; then
return 0;
fi
if [ -z "${haystack##*$needle*}" ]; then
return 0
else
[ "${#msg}" -gt 0 ] && log_failure "$haystack doesn't contain $needle :: $msg" || true
return 1
fi
}
# Проверяет, что подстрока не содержится в строке.
#
# Arguments:
#
# * haystack — строка, в которой выполняется поиск.
# * needle — подстрока, отсутствие которой проверяется.
# * msg — необязательное сообщение, выводимое при неудаче.
#
# Returns:
#
# * 0, если needle не содержится в haystack или needle пуста.
# * 1, если needle содержится в haystack (и при этом выводится сообщение об ошибке, если оно указано).
#
# Example:
#
# ```bash
# assert_not_contain "abcdef" "gh" # вернёт 0
# assert_not_contain "abcdef" "cd" # вернёт 1
# ```
assert_not_contain() {
local haystack="$1"
local needle="${2-}"
local msg="${3-}"
if [ -z "${needle:+x}" ]; then
return 0;
fi
if [ "${haystack##*$needle*}" ]; then
return 0
else
[ "${#msg}" -gt 0 ] && log_failure "$haystack contains $needle :: $msg" || true
return 1
fi
}
# Проверяет, что первое число больше второго.
#
# Arguments:
#
# * first — первое сравниваемое число
# * second — второе сравниваемое число
# * msg (необязательно) — сообщение, выводимое при неудаче проверки
#
# Returns:
#
# * 0, если first больше second; иначе 1
#
# Example:
#
# ```bash
# assert_gt 5 3 "5 должно быть больше 3"
# ```
assert_gt() {
local first="$1"
local second="$2"
local msg="${3-}"
if [[ "$first" -gt "$second" ]]; then
return 0
else
[ "${#msg}" -gt 0 ] && log_failure "$first > $second :: $msg" || true
return 1
fi
}
# Проверяет, что первое число больше или равно второму.
#
# Arguments:
#
# * first — первое сравниваемое число.
# * second — второе сравниваемое число.
# * msg (необязательно) — сообщение, выводимое при ошибке.
#
# Returns:
#
# * 0, если first >= second; иначе 1 и сообщение об ошибке.
#
# Example:
#
# assert_ge 10 5 "Ожидалось, что 10 больше или равно 5"
assert_ge() {
local first="$1"
local second="$2"
local msg="${3-}"
if [[ "$first" -ge "$second" ]]; then
return 0
else
[ "${#msg}" -gt 0 ] && log_failure "$first >= $second :: $msg" || true
return 1
fi
}
# Проверяет, что первое число меньше второго.
#
# Arguments:
#
# * first — первое сравниваемое число.
# * second — второе сравниваемое число.
# * msg (необязательно) — сообщение, выводимое при ошибке.
#
# Returns:
#
# * 0, если first < second; иначе 1 и сообщение об ошибке.
#
# Example:
#
# ```bash
# assert_lt 3 5 "3 должно быть меньше 5"
# ```
assert_lt() {
local first="$1"
local second="$2"
local msg="${3-}"
if [[ "$first" -lt "$second" ]]; then
return 0
else
[ "${#msg}" -gt 0 ] && log_failure "$first < $second :: $msg" || true
return 1
fi
}
# Проверяет, что первое число меньше или равно второму.
#
# Arguments:
#
# * first — первое сравниваемое число
# * second — второе сравниваемое число
# * msg (необязательно) — сообщение, выводимое при неудаче
#
# Returns:
#
# * 0, если first ≤ second; иначе 1 и сообщение об ошибке (если указано)
#
# Example:
#
# assert_le 3 5 "3 не больше 5" # успешно
# assert_le 7 2 "7 не меньше 2" # завершится с ошибкой и выведет сообщение
assert_le() {
local first="$1"
local second="$2"
local msg="${3-}"
if [[ "$first" -le "$second" ]]; then
return 0
else
[ "${#msg}" -gt 0 ] && log_failure "$first <= $second :: $msg" || true
return 1
fi
}