diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..cefa5bbdc9 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.git +.DS_Store +.vscode +stage +include/ost-config.php +include/mpdf/tmp +include/mpdf/ttfontdata diff --git a/.env.docker.example b/.env.docker.example new file mode 100644 index 0000000000..e32e76217d --- /dev/null +++ b/.env.docker.example @@ -0,0 +1,8 @@ +OSTICKET_HTTP_PORT=8080 +OSTICKET_DEPLOY_ON_START=1 +OSTICKET_DEFAULT_LANG=pt_BR +MYSQL_DATABASE=osticket +MYSQL_USER=osticket +MYSQL_PASSWORD=osticket +MYSQL_ROOT_PASSWORD=root +TZ=America/Sao_Paulo diff --git a/.github/workflows/publish-image.yml b/.github/workflows/publish-image.yml new file mode 100644 index 0000000000..e65947e4c3 --- /dev/null +++ b/.github/workflows/publish-image.yml @@ -0,0 +1,62 @@ +name: Publish Docker Image + +on: + push: + branches: + - '**' + tags: + - 'v*' + pull_request: + workflow_dispatch: + +concurrency: + group: publish-image-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + packages: write + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GHCR + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=sha,prefix=sha- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and publish image + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitignore b/.gitignore index 7769425590..736a06a421 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ stage include/mpdf/ttfontdata include/mpdf/tmp nbproject/ +.env.docker diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000000..cffd55edac --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +FROM php:8.3-apache-bookworm + +ENV APACHE_DOCUMENT_ROOT=/var/www/html \ + OSTICKET_APP=/var/www/html \ + OSTICKET_SRC=/srv/osticket-src + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + git \ + libc-client2007e-dev \ + libfreetype6-dev \ + libicu-dev \ + libjpeg62-turbo-dev \ + libkrb5-dev \ + libpng-dev \ + libxml2-dev \ + libzip-dev \ + && docker-php-source extract \ + && docker-php-ext-configure gd --with-freetype --with-jpeg \ + && ac_cv_utf8_mime2text=new ac_cv_u8t_decompose=yes docker-php-ext-configure imap --with-kerberos --with-imap-ssl \ + && docker-php-ext-install -j"$(nproc)" \ + gd \ + gettext \ + imap \ + intl \ + mysqli \ + opcache \ + zip \ + && pecl install apcu \ + && docker-php-ext-enable apcu \ + && docker-php-source delete \ + && a2enmod expires headers rewrite \ + && sed -ri "s!/var/www/html!${APACHE_DOCUMENT_ROOT}!g" \ + /etc/apache2/apache2.conf \ + /etc/apache2/conf-available/*.conf \ + /etc/apache2/sites-available/*.conf \ + && rm -rf /var/lib/apt/lists/* + +COPY . /srv/osticket-src +COPY docker/i18n/ /opt/osticket-i18n/ +COPY docker/apache/osticket.conf /etc/apache2/conf-available/osticket.conf +COPY docker/php/osticket.ini /usr/local/etc/php/conf.d/osticket.ini +COPY docker/entrypoint.sh /usr/local/bin/osticket-entrypoint + +RUN chmod +x /usr/local/bin/osticket-entrypoint \ + && a2enconf osticket \ + && mkdir -p /var/www/html + +WORKDIR /var/www/html + +ENTRYPOINT ["osticket-entrypoint"] +CMD ["apache2-foreground"] diff --git a/README.docker.md b/README.docker.md new file mode 100644 index 0000000000..45f69c8887 --- /dev/null +++ b/README.docker.md @@ -0,0 +1,54 @@ +# osTicket com Docker + +Este repositório agora inclui um ambiente local com `docker compose` para subir o osTicket com `PHP 8.4 + Apache` e `MariaDB 10.11`. + +## Como subir + +1. Copie o arquivo de exemplo: + + ```bash + cp .env.docker.example .env.docker + ``` + +2. Suba os containers: + + ```bash + docker compose --env-file .env.docker up --build + ``` + +3. Abra: + + ```text + http://localhost:8080/setup/ + ``` + +## Banco para o instalador + +Use os valores abaixo no instalador web, a menos que tenha alterado o `.env.docker`: + +- Host: `db` +- Database: `osticket` +- User: `osticket` +- Password: `osticket` + +## Como funciona + +- O container `web` instala as extensoes PHP exigidas pelo projeto, incluindo `imap`. +- O container tambem habilita `APCu` para melhorar cache e desempenho. +- No startup, o entrypoint executa `php manage.php deploy --setup /var/www/html`. +- O codigo-fonte local e montado em `/srv/osticket-src` apenas para servir de origem ao `deploy`. +- O deploy final fica persistido no volume `osticket-app`. +- O banco fica persistido no volume `osticket-db`. + +## Atualizando o codigo + +Por padrao, o container reaplica o `deploy` a cada start com `OSTICKET_DEPLOY_ON_START=1`. Se quiser desligar esse comportamento, ajuste a variavel no `.env.docker`. + +## Pos-instalacao + +Depois que a instalacao terminar, vale remover a pasta `setup` do volume implantado e travar o arquivo de configuracao: + +```bash +docker compose exec web rm -rf /var/www/html/setup +docker compose exec web chmod 0644 /var/www/html/include/ost-config.php +``` diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..6a716648dc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +services: + web: + build: + context: . + depends_on: + db: + condition: service_healthy + environment: + OSTICKET_APP: /var/www/html + OSTICKET_DB_HOST: ${OSTICKET_DB_HOST:-db} + OSTICKET_DB_NAME: ${MYSQL_DATABASE:-osticket} + OSTICKET_DB_PASS: ${MYSQL_PASSWORD:-osticket} + OSTICKET_DB_PREFIX: ${OSTICKET_DB_PREFIX:-ost_} + OSTICKET_DB_USER: ${MYSQL_USER:-osticket} + OSTICKET_DEFAULT_LANG: ${OSTICKET_DEFAULT_LANG:-pt_BR} + OSTICKET_DEPLOY_ON_START: ${OSTICKET_DEPLOY_ON_START:-1} + OSTICKET_SRC: /srv/osticket-src + TZ: ${TZ:-America/Sao_Paulo} + ports: + - "${OSTICKET_HTTP_PORT:-8080}:80" + volumes: + - .:/srv/osticket-src:ro + - osticket-app:/var/www/html + restart: unless-stopped + + db: + image: mariadb:10.11 + command: + - --character-set-server=utf8mb4 + - --collation-server=utf8mb4_unicode_ci + environment: + MYSQL_DATABASE: ${MYSQL_DATABASE:-osticket} + MYSQL_PASSWORD: ${MYSQL_PASSWORD:-osticket} + MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD:-root} + MYSQL_USER: ${MYSQL_USER:-osticket} + TZ: ${TZ:-America/Sao_Paulo} + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 20s + restart: unless-stopped + volumes: + - osticket-db:/var/lib/mysql + +volumes: + osticket-app: + osticket-db: diff --git a/docker/apache/osticket.conf b/docker/apache/osticket.conf new file mode 100644 index 0000000000..395006b7f0 --- /dev/null +++ b/docker/apache/osticket.conf @@ -0,0 +1,5 @@ + + AllowOverride All + Options FollowSymLinks + Require all granted + diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100644 index 0000000000..df4da1c405 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,93 @@ +#!/bin/sh +set -eu + +OSTICKET_APP="${OSTICKET_APP:-/var/www/html}" +OSTICKET_SRC="${OSTICKET_SRC:-/srv/osticket-src}" +OSTICKET_DEPLOY_ON_START="${OSTICKET_DEPLOY_ON_START:-1}" +OSTICKET_CONFIG="${OSTICKET_APP}/include/ost-config.php" +OSTICKET_I18N_SRC="${OSTICKET_I18N_SRC:-/opt/osticket-i18n}" +OSTICKET_SAMPLE_CONFIG="${OSTICKET_APP}/include/ost-sampleconfig.php" +OSTICKET_SETUP_DIR="${OSTICKET_APP}/setup" + +deploy_osticket() { + if [ ! -f "${OSTICKET_SRC}/manage.php" ]; then + echo "osTicket source not found at ${OSTICKET_SRC}" >&2 + exit 1 + fi + + git config --global --add safe.directory "${OSTICKET_SRC}" >/dev/null 2>&1 || true + + mkdir -p "${OSTICKET_APP}" + ( + cd "${OSTICKET_SRC}" + php manage.php deploy --setup "${OSTICKET_APP}" + ) +} + +prepare_config() { + if [ ! -f "${OSTICKET_CONFIG}" ] && [ -f "${OSTICKET_SAMPLE_CONFIG}" ]; then + cp "${OSTICKET_SAMPLE_CONFIG}" "${OSTICKET_CONFIG}" + fi + + if [ -f "${OSTICKET_CONFIG}" ]; then + if grep -q "%CONFIG-DBHOST" "${OSTICKET_CONFIG}"; then + chmod 0666 "${OSTICKET_CONFIG}" + else + chmod 0644 "${OSTICKET_CONFIG}" + fi + fi +} + +prepare_runtime_dirs() { + mkdir -p \ + "${OSTICKET_APP}/include/i18n" \ + "${OSTICKET_APP}/include/mpdf/tmp" \ + "${OSTICKET_APP}/include/mpdf/ttfontdata" + chown -R www-data:www-data "${OSTICKET_APP}" +} + +install_language_packs() { + if [ -d "${OSTICKET_I18N_SRC}" ]; then + find "${OSTICKET_I18N_SRC}" -maxdepth 1 -type f -name '*.phar' -exec cp {} "${OSTICKET_APP}/include/i18n/" \; + fi +} + +installation_complete() { + [ -f "${OSTICKET_CONFIG}" ] \ + && ! grep -q "define('OSTINSTALLED',FALSE);" "${OSTICKET_CONFIG}" 2>/dev/null +} + +finalize_installation() { + chmod 0644 "${OSTICKET_CONFIG}" 2>/dev/null || true + rm -rf "${OSTICKET_SETUP_DIR}" 2>/dev/null || true +} + +finalize_after_install() { + ( + while [ ! -f "${OSTICKET_CONFIG}" ]; do + sleep 2 + done + + while ! installation_complete; do + sleep 2 + done + + finalize_installation + ) & +} + +if [ ! -f "${OSTICKET_APP}/index.php" ] || [ "${OSTICKET_DEPLOY_ON_START}" = "1" ]; then + deploy_osticket +fi + +prepare_config +prepare_runtime_dirs +install_language_packs + +if installation_complete; then + finalize_installation +else + finalize_after_install +fi + +exec "$@" diff --git a/docker/i18n/pt_BR.phar b/docker/i18n/pt_BR.phar new file mode 100644 index 0000000000..c39c70a40b Binary files /dev/null and b/docker/i18n/pt_BR.phar differ diff --git a/docker/php/osticket.ini b/docker/php/osticket.ini new file mode 100644 index 0000000000..b4090a8b03 --- /dev/null +++ b/docker/php/osticket.ini @@ -0,0 +1,6 @@ +date.timezone=America/Sao_Paulo +file_uploads=On +max_execution_time=120 +memory_limit=256M +post_max_size=32M +upload_max_filesize=32M diff --git a/setup/inc/install.inc.php b/setup/inc/install.inc.php index 2e61782d2d..c04b7d2924 100644 --- a/setup/inc/install.inc.php +++ b/setup/inc/install.inc.php @@ -1,6 +1,18 @@ 'ost_','dbhost'=>'localhost','lang_id'=>'en_US'); + +$defaults = array( + 'prefix' => getenv('OSTICKET_DB_PREFIX') ?: 'ost_', + 'dbhost' => getenv('OSTICKET_DB_HOST') ?: 'localhost', + 'dbname' => getenv('OSTICKET_DB_NAME') ?: '', + 'dbuser' => getenv('OSTICKET_DB_USER') ?: '', + 'dbpass' => getenv('OSTICKET_DB_PASS') ?: '', + 'lang_id' => getenv('OSTICKET_DEFAULT_LANG') ?: 'en_US', +); + +$info = ($_POST && $errors) + ? Format::htmlchars($_POST) + : $defaults; ?>