Skip to content

Instantly share code, notes, and snippets.

@mpociot
Created June 4, 2025 09:06
Show Gist options
  • Save mpociot/1d6809a600701beca4fa544551acd2de to your computer and use it in GitHub Desktop.
Save mpociot/1d6809a600701beca4fa544551acd2de to your computer and use it in GitHub Desktop.
OpenAI Codex PHP environment setup for Laravel
/bin/bash -c "$(curl -fsSL https://php.new/install/linux)"
export PATH="/root/.config/herd-lite/bin/:$PATH"
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate --seed --force -n
npm install
npm run build
@ndeblauw
Copy link

ndeblauw commented Jun 5, 2025

Thanks for this script. How do you deal with the DB? I'm not managing to get mysql nor sqlite working out of the box on codex. Did you add a special feature somewhere else?

Additionally, why npm install and not npm install --no-save (which would avoid merging problems, no?) and why composer install and not composer install --no-interaction --prefer-dist --optimize-autoloader (which has less chance in getting blocked)? Just asking to learn and to see whether it can be improved.

@trimrx
Copy link

trimrx commented Jun 11, 2025

here is the version with MySQL
IT WORKS

`#!/usr/bin/env bash

this makes the script strict:

-e: exit on any error

-u: error on undefined variables

-o pipefail: exit if any command in a pipeline fails

set -euo pipefail

Adds a package repository that provides the latest PHP versions

(see https://deb.sury.org/ for details)

add-apt-repository -y ppa:ondrej/php

Updates package lists to get the latest information about available packages

apt-get update

Install PHP 8.4 and extensions commonly needed for Symfony applications

apt-get install -y
php8.4
php8.4-cli
php8.4-mbstring
php8.4-xml
php8.4-intl
php8.4-gd
php8.4-zip
php8.4-curl
php8.4-mysql # ✅ Also switch from pgsql to mysql now

This makes PHP 8.4 available through the global 'php' binary,

which is expected by many commands

update-alternatives --install /usr/bin/php php /usr/bin/php8.4 84
update-alternatives --set php /usr/bin/php8.4

Install Composer as a global 'composer' binary following the safest practices

EXPECTED_CHECKSUM="$(curl -fsSL https://composer.github.io/installer.sig)"
curl -fsSL https://getcomposer.org/installer -o composer-setup.php
ACTUAL_CHECKSUM="$(sha384sum composer-setup.php | cut -d ' ' -f 1)"
if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then
echo 'ERROR: Invalid composer installer checksum.' >&2
rm composer-setup.php
exit 1
fi
php composer-setup.php --install-dir=/usr/local/bin --filename=composer --quiet
rm composer-setup.php

Install the PHP dependencies of your project;

private packages require some more work explained later in this article

composer install --ignore-platform-req=ext-bcmath

Show installed versions to check if everything worked

php -v
composer --version

Load Laravel-style environment variables

DB_DATABASE="${DB_DATABASE:-mydb}"
DB_USERNAME="${DB_USERNAME:-myuser}"
DB_PASSWORD="${DB_PASSWORD:-mypassword}"

Default MySQL root password (optional, can be customized)

MYSQL_ROOT_PASSWORD="${MYSQL_ROOT_PASSWORD:-root}"

apt-get install -y mysql-client mysql-common mysql-server-core-8.0

mkdir -p /tmp/mysql-data
mysqld --initialize-insecure --datadir=/tmp/mysql-data

Fix for secure-file-priv error

mkdir -p /var/lib/mysql-files

Start MySQL manually

nohup mysqld --user=root --datadir=/tmp/mysql-data --socket=/tmp/mysql.sock > /tmp/mysql.log 2>&1 &

Wait for socket

echo "Waiting for MySQL to be ready..."
for i in {30..0}; do
if [ -S /tmp/mysql.sock ]; then
echo "✅ MySQL socket is ready"
break
fi
sleep 1
done

if ! [ -S /tmp/mysql.sock ]; then
echo "❌ MySQL failed to start"
tail -n 50 /tmp/mysql.log || true
exit 1
fi

Setup user/db

mysql -u root --socket=/tmp/mysql.sock <<-EOSQL
CREATE DATABASE IF NOT EXISTS `${DB_DATABASE}`;
CREATE USER IF NOT EXISTS '${DB_USERNAME}'@'localhost' IDENTIFIED BY '${DB_PASSWORD}';
GRANT ALL PRIVILEGES ON `${DB_DATABASE}`.* TO '${DB_USERNAME}'@'localhost';
FLUSH PRIVILEGES;
EOSQL

Show installed MySQL version

mysql --version

cp .env.example .env
php artisan key:generate
php artisan migrate --seed --force -n
npm install --no-save
npm run build`

@jimrubenstein
Copy link

here is the version with MySQL IT WORKS

link to the original article:

https://dev.to/javiereguiluz/how-to-make-chatgpt-codex-work-with-php-and-symfony-4lj8

@AniTexs
Copy link

AniTexs commented Jul 22, 2025

I've created a script here that uses

  • MySQL
  • Meilisearch
  • Yarn (Instead of NPM)

Codex Environment Settings:

  • Agent internet access => Off
  • Image => universal
  • Secrets => None
  • Environment variables => None

Here the script

#!/usr/bin/env bash

# Fail fast, fail loud.
set -euo pipefail

################################################################################
# 1. PHP & Composer ############################################################
################################################################################

add-apt-repository -y ppa:ondrej/php
apt-get update

apt-get install -y \
  php8.4 php8.4-cli php8.4-mbstring php8.4-xml php8.4-intl \
  php8.4-gd  php8.4-zip  php8.4-curl  php8.4-mysql \
  curl gnupg lsb-release ca-certificates

update-alternatives --install /usr/bin/php php /usr/bin/php8.4 84
update-alternatives --set php /usr/bin/php8.4

EXPECTED_CHECKSUM="$(curl -fsSL https://composer.github.io/installer.sig)"
curl -fsSL https://getcomposer.org/installer -o composer-setup.php
ACTUAL_CHECKSUM="$(sha384sum composer-setup.php | cut -d ' ' -f 1)"
[ "$EXPECTED_CHECKSUM" = "$ACTUAL_CHECKSUM" ] || { echo "Composer installer corrupt" >&2; exit 1; }
php composer-setup.php --install-dir=/usr/local/bin --filename=composer --quiet
rm composer-setup.php

composer install --ignore-platform-req=ext-bcmath

php -v; composer -V

################################################################################
# 2. Node.js & Yarn ############################################################
################################################################################

NODE_VERSION="${NODE_VERSION:-20}"
YARN_VERSION="${YARN_VERSION:-1.22.22}"

# Install the desired Node LTS via NodeSource
curl -fsSL "https://deb.nodesource.com/setup_${NODE_VERSION}.x" | bash -
apt-get install -y nodejs

# ---------------------------------------------------------------------------
# Yarn (via Corepack)
# ---------------------------------------------------------------------------
# Node ≥16 ships with Corepack, which can activate pinned versions of Yarn.
# This avoids npm‑global install conflicts and sticks to a reproducible version.

if ! command -v corepack >/dev/null 2>&1; then
  # Fallback for odd Node builds that omit Corepack
  npm install -g corepack
fi

corepack enable
corepack prepare "yarn@${YARN_VERSION}" --activate

node -v; yarn -v

################################################################################
# 3. MySQL (local, socket‑based) ###############################################
################################################################################

DB_DATABASE="${DB_DATABASE:-mydb}"
DB_USERNAME="${DB_USERNAME:-myuser}"
DB_PASSWORD="${DB_PASSWORD:-mypassword}"

export DB_CONNECTION=mysql
export DB_HOST=127.0.0.1
export DB_PORT=3306
export DB_DATABASE DB_USERNAME DB_PASSWORD

apt-get install -y mysql-client mysql-common mysql-server-core-8.0

MYSQL_DATADIR="/tmp/mysql-data"; MYSQL_SOCKET="/tmp/mysql.sock"
mkdir -p "$MYSQL_DATADIR"
mysqld --initialize-insecure --datadir="$MYSQL_DATADIR"
mkdir -p /var/lib/mysql-files
nohup mysqld --user=root --datadir="$MYSQL_DATADIR" --socket="$MYSQL_SOCKET" \
      > /tmp/mysql.log 2>&1 &

printf "Waiting for MySQL"; for _ in {30..0}; do
  if mysqladmin --socket="$MYSQL_SOCKET" ping --silent 2>/dev/null; then
    echo -e "\r✅ MySQL ready            "; break; fi
  printf "."; sleep 1; done
mysqladmin --socket="$MYSQL_SOCKET" ping --silent >/dev/null || { tail -n 50 /tmp/mysql.log; exit 1; }

mysql -u root --socket="$MYSQL_SOCKET" <<SQL
CREATE DATABASE IF NOT EXISTS \`$DB_DATABASE\`;
CREATE USER IF NOT EXISTS '$DB_USERNAME'@'%' IDENTIFIED BY '$DB_PASSWORD';
GRANT ALL PRIVILEGES ON \`$DB_DATABASE\`.* TO '$DB_USERNAME'@'%';
FLUSH PRIVILEGES;
SQL

mysql --version

################################################################################
# 4. Meilisearch (needed by seeders) ###########################################
################################################################################

MEILI_VERSION="${MEILI_VERSION:-latest}"
MEILI_MASTER_KEY="${MEILI_MASTER_KEY:-masterKey}"
MEILISEARCH_BIN="/usr/local/bin/meilisearch"
MEILISEARCH_HOST="http://127.0.0.1:7700"

if [ ! -x "$MEILISEARCH_BIN" ]; then
  echo "Installing Meilisearch ($MEILI_VERSION) …"
  if [ "$MEILI_VERSION" = "latest" ]; then
    curl -L https://install.meilisearch.com | sh
  else
    ASSET_URL="https://github.com/meilisearch/meilisearch/releases/download/${MEILI_VERSION}/meilisearch-linux-amd64"
    if ! curl -fSL "$ASSET_URL" -o meilisearch; then
      echo "⚠️  Version $MEILI_VERSION not found – falling back to latest" >&2
      curl -L https://install.meilisearch.com | sh
    fi
  fi
  chmod +x ./meilisearch
  mv ./meilisearch "$MEILISEARCH_BIN"
fi

export MEILISEARCH_HOST MEILISEARCH_KEY="$MEILI_MASTER_KEY"
nohup "$MEILISEARCH_BIN" --master-key "$MEILI_MASTER_KEY" --http-addr "127.0.0.1:7700" \
     > /tmp/meilisearch.log 2>&1 &

printf "Waiting for Meilisearch"; for _ in {30..0}; do
  if curl -sf "$MEILISEARCH_HOST/health" >/dev/null; then
    echo -e "\r✅ Meilisearch healthy       "; break; fi
  printf "."; sleep 1; done
curl -sf "$MEILISEARCH_HOST/health" >/dev/null || { echo; tail -n 50 /tmp/meilisearch.log; exit 1; }

################################################################################
# 5. Build the Laravel application ############################################
################################################################################

cp .env.example .env

update_env() {
  local key="$1"
  local value="$2"

  # Escape replacement string for sed (handles /, \ and &)
  local escaped_value
  escaped_value=$(printf '%s' "$value" | sed -e 's/[\\/&]/\\&/g')

  if grep -q "^$key=" .env; then
    sed -i "s|^$key=.*|$key=$escaped_value|" .env
  else
    echo "$key=$value" >> .env
  fi
}

update_env DB_HOST 127.0.0.1
update_env DB_DATABASE "$DB_DATABASE"
update_env DB_USERNAME "$DB_USERNAME"
update_env DB_PASSWORD "$DB_PASSWORD"
update_env MEILISEARCH_HOST "$MEILISEARCH_HOST"
update_env MEILISEARCH_KEY "$MEILI_MASTER_KEY"

php artisan key:generate
php artisan migrate --seed --force -n

# JavaScript dependencies & build (Yarn Classic tolerates peer conflicts)
yarn install --non-interactive --no-progress

yarn run build

################################################################################
# Done #########################################################################
################################################################################

@AniTexs
Copy link

AniTexs commented Jul 22, 2025

I've created a script here that uses

  • MySQL
  • Meilisearch
  • Yarn (Instead of NPM)

Codex Environment Settings:

  • Agent internet access => Off
  • Image => universal
  • Secrets => None
  • Environment variables => None

Here the script

#!/usr/bin/env bash

# Fail fast, fail loud.
set -euo pipefail

################################################################################
# 1. PHP & Composer ############################################################
################################################################################

add-apt-repository -y ppa:ondrej/php
apt-get update

apt-get install -y \
  php8.4 php8.4-cli php8.4-mbstring php8.4-xml php8.4-intl \
  php8.4-gd  php8.4-zip  php8.4-curl  php8.4-mysql \
  curl gnupg lsb-release ca-certificates

update-alternatives --install /usr/bin/php php /usr/bin/php8.4 84
update-alternatives --set php /usr/bin/php8.4

EXPECTED_CHECKSUM="$(curl -fsSL https://composer.github.io/installer.sig)"
curl -fsSL https://getcomposer.org/installer -o composer-setup.php
ACTUAL_CHECKSUM="$(sha384sum composer-setup.php | cut -d ' ' -f 1)"
[ "$EXPECTED_CHECKSUM" = "$ACTUAL_CHECKSUM" ] || { echo "Composer installer corrupt" >&2; exit 1; }
php composer-setup.php --install-dir=/usr/local/bin --filename=composer --quiet
rm composer-setup.php

composer install --ignore-platform-req=ext-bcmath

php -v; composer -V

################################################################################
# 2. Node.js & Yarn ############################################################
################################################################################

NODE_VERSION="${NODE_VERSION:-20}"
YARN_VERSION="${YARN_VERSION:-1.22.22}"

# Install the desired Node LTS via NodeSource
curl -fsSL "https://deb.nodesource.com/setup_${NODE_VERSION}.x" | bash -
apt-get install -y nodejs

# ---------------------------------------------------------------------------
# Yarn (via Corepack)
# ---------------------------------------------------------------------------
# Node ≥16 ships with Corepack, which can activate pinned versions of Yarn.
# This avoids npm‑global install conflicts and sticks to a reproducible version.

if ! command -v corepack >/dev/null 2>&1; then
  # Fallback for odd Node builds that omit Corepack
  npm install -g corepack
fi

corepack enable
corepack prepare "yarn@${YARN_VERSION}" --activate

node -v; yarn -v

################################################################################
# 3. MySQL (local, socket‑based) ###############################################
################################################################################

DB_DATABASE="${DB_DATABASE:-mydb}"
DB_USERNAME="${DB_USERNAME:-myuser}"
DB_PASSWORD="${DB_PASSWORD:-mypassword}"

export DB_CONNECTION=mysql
export DB_HOST=127.0.0.1
export DB_PORT=3306
export DB_DATABASE DB_USERNAME DB_PASSWORD

apt-get install -y mysql-client mysql-common mysql-server-core-8.0

MYSQL_DATADIR="/tmp/mysql-data"; MYSQL_SOCKET="/tmp/mysql.sock"
mkdir -p "$MYSQL_DATADIR"
mysqld --initialize-insecure --datadir="$MYSQL_DATADIR"
mkdir -p /var/lib/mysql-files
nohup mysqld --user=root --datadir="$MYSQL_DATADIR" --socket="$MYSQL_SOCKET" \
      > /tmp/mysql.log 2>&1 &

printf "Waiting for MySQL"; for _ in {30..0}; do
  if mysqladmin --socket="$MYSQL_SOCKET" ping --silent 2>/dev/null; then
    echo -e "\r✅ MySQL ready            "; break; fi
  printf "."; sleep 1; done
mysqladmin --socket="$MYSQL_SOCKET" ping --silent >/dev/null || { tail -n 50 /tmp/mysql.log; exit 1; }

mysql -u root --socket="$MYSQL_SOCKET" <<SQL
CREATE DATABASE IF NOT EXISTS \`$DB_DATABASE\`;
CREATE USER IF NOT EXISTS '$DB_USERNAME'@'%' IDENTIFIED BY '$DB_PASSWORD';
GRANT ALL PRIVILEGES ON \`$DB_DATABASE\`.* TO '$DB_USERNAME'@'%';
FLUSH PRIVILEGES;
SQL

mysql --version

################################################################################
# 4. Meilisearch (needed by seeders) ###########################################
################################################################################

MEILI_VERSION="${MEILI_VERSION:-latest}"
MEILI_MASTER_KEY="${MEILI_MASTER_KEY:-masterKey}"
MEILISEARCH_BIN="/usr/local/bin/meilisearch"
MEILISEARCH_HOST="http://127.0.0.1:7700"

if [ ! -x "$MEILISEARCH_BIN" ]; then
  echo "Installing Meilisearch ($MEILI_VERSION) …"
  if [ "$MEILI_VERSION" = "latest" ]; then
    curl -L https://install.meilisearch.com | sh
  else
    ASSET_URL="https://github.com/meilisearch/meilisearch/releases/download/${MEILI_VERSION}/meilisearch-linux-amd64"
    if ! curl -fSL "$ASSET_URL" -o meilisearch; then
      echo "⚠️  Version $MEILI_VERSION not found – falling back to latest" >&2
      curl -L https://install.meilisearch.com | sh
    fi
  fi
  chmod +x ./meilisearch
  mv ./meilisearch "$MEILISEARCH_BIN"
fi

export MEILISEARCH_HOST MEILISEARCH_KEY="$MEILI_MASTER_KEY"
nohup "$MEILISEARCH_BIN" --master-key "$MEILI_MASTER_KEY" --http-addr "127.0.0.1:7700" \
     > /tmp/meilisearch.log 2>&1 &

printf "Waiting for Meilisearch"; for _ in {30..0}; do
  if curl -sf "$MEILISEARCH_HOST/health" >/dev/null; then
    echo -e "\r✅ Meilisearch healthy       "; break; fi
  printf "."; sleep 1; done
curl -sf "$MEILISEARCH_HOST/health" >/dev/null || { echo; tail -n 50 /tmp/meilisearch.log; exit 1; }

################################################################################
# 5. Build the Laravel application ############################################
################################################################################

cp .env.example .env

update_env() {
  local key="$1"
  local value="$2"

  # Escape replacement string for sed (handles /, \ and &)
  local escaped_value
  escaped_value=$(printf '%s' "$value" | sed -e 's/[\\/&]/\\&/g')

  if grep -q "^$key=" .env; then
    sed -i "s|^$key=.*|$key=$escaped_value|" .env
  else
    echo "$key=$value" >> .env
  fi
}

update_env DB_HOST 127.0.0.1
update_env DB_DATABASE "$DB_DATABASE"
update_env DB_USERNAME "$DB_USERNAME"
update_env DB_PASSWORD "$DB_PASSWORD"
update_env MEILISEARCH_HOST "$MEILISEARCH_HOST"
update_env MEILISEARCH_KEY "$MEILI_MASTER_KEY"

php artisan key:generate
php artisan migrate --seed --force -n

# JavaScript dependencies & build (Yarn Classic tolerates peer conflicts)
yarn install --non-interactive --no-progress

yarn run build

################################################################################
# Done #########################################################################
################################################################################

Here's a smaller version which pulls from my Gist's.

#!/usr/bin/env bash

###############################################################################
# Codex Laravel: lightweight orchestrator ######################################
###############################################################################
# This tiny entry‑point just stitches together a handful of *modular* scripts
# you now keep in public GitHub gists. Each gist handles one concern (PHP/Composer,
# Yarn/Node, MySQL, Meilisearch).  The big upside is that you can iterate on any
# of those pieces in isolation without touching this file.
###############################################################################

set -euo pipefail

###############################################################################
# 0. Defaults & env ...........................................................
###############################################################################

# -----------------------------------------------------------------------------
# Database credentials (used by both MySQL & Laravel)
# -----------------------------------------------------------------------------
DB_DATABASE="${DB_DATABASE:-mydb}"
DB_USERNAME="${DB_USERNAME:-myuser}"
DB_PASSWORD="${DB_PASSWORD:-mypassword}"
export DB_DATABASE DB_USERNAME DB_PASSWORD

# -----------------------------------------------------------------------------
# Node/Yarn versions (used by the Yarn setup gist)
# -----------------------------------------------------------------------------
NODE_VERSION="${NODE_VERSION:-20}"
YARN_VERSION="${YARN_VERSION:-1.22.22}"
export NODE_VERSION YARN_VERSION

# -----------------------------------------------------------------------------
# Meilisearch versions / credentials
# -----------------------------------------------------------------------------
MEILI_VERSION="${MEILI_VERSION:-latest}"
MEILI_MASTER_KEY="${MEILI_MASTER_KEY:-masterKey}"
export MEILI_VERSION MEILI_MASTER_KEY

###############################################################################
# Helper – download & execute a gist ..........................................
###############################################################################
run_gist() {
  local raw_url="$1"
  echo -e "\n🔽  Running remote gist: $raw_url\n"
  curl -fsSL "$raw_url" | bash -s --
}

###############################################################################
# 1. PHP & Composer ............................................................
###############################################################################
run_gist "https://gist.githubusercontent.com/AniTexs/63c116ccbabc78831fd45ad94398bfa3/raw"

###############################################################################
# 2. Node.js & Yarn ............................................................
###############################################################################
run_gist "https://gist.githubusercontent.com/AniTexs/5a31efa79f6b5708449a3067cd12d25a/raw"

###############################################################################
# 3. MySQL (local) .............................................................
###############################################################################
run_gist "https://gist.githubusercontent.com/AniTexs/10ce4272a96763c53c4e46bfbd91c49c/raw"

###############################################################################
# 4. Meilisearch ...............................................................
###############################################################################
run_gist "https://gist.githubusercontent.com/AniTexs/99e66d818a21cdce70c6c0f3f256c99a/raw"

###############################################################################
# 5. Build Laravel app ........................................................
###############################################################################

# -- .env ----------------------------------------------------------------------
cp .env.example .env

update_env() {
  local key="$1"; local value="$2"
  local escaped_value
  escaped_value=$(printf '%s' "$value" | sed -e 's/[\\/&]/\\&/g')
  if grep -q "^$key=" .env; then
    sed -i "s|^$key=.*|$key=$escaped_value|" .env
  else
    echo "$key=$value" >> .env
  fi
}

update_env DB_HOST 127.0.0.1
update_env DB_DATABASE "$DB_DATABASE"
update_env DB_USERNAME "$DB_USERNAME"
update_env DB_PASSWORD "$DB_PASSWORD"
update_env MEILISEARCH_HOST "http://127.0.0.1:7700"
update_env MEILISEARCH_KEY "$MEILI_MASTER_KEY"

# -- Laravel -------------------------------------------------------------------
php artisan key:generate
php artisan migrate --seed --force -n

# -- JavaScript deps & build ----------------------------------------------------
yarn install --non-interactive --no-progress
yarn run build

###############################################################################
# Done #########################################################################
###############################################################################

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment