diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..4d2ea23 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,53 @@ +FROM mcr.microsoft.com/devcontainers/base:trixie + +# Install libaio1t64 and provide a libaio.so.1 symlink for Oracle Instant Client +# Install rlwrap for readline support in sqlplus +RUN rm -f /etc/apt/sources.list.d/yarn.list \ + && apt-get update \ + && apt-get install -y libaio1t64 rlwrap \ + && rm -rf /var/lib/apt/lists/* \ + && DEB_HOST_MULTIARCH="$(dpkg-architecture -qDEB_HOST_MULTIARCH)" \ + && ln -sf /usr/lib/${DEB_HOST_MULTIARCH}/libaio.so.1t64 \ + /usr/lib/${DEB_HOST_MULTIARCH}/libaio.so.1 \ + && echo "alias sqlplus='rlwrap sqlplus'" >> /etc/bash.bashrc + +# Create directory structure for Oracle +RUN mkdir -p /opt/oracle + +# Download and install Oracle Instant Client based on architecture. +# x86_64 uses Oracle's version-less "always latest" URLs; arm64 must pin a +# version because Oracle does not publish version-less arm64 zips. +WORKDIR /tmp +RUN ARCH=$(uname -m) && \ + if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then \ + IC_VERSION="23.26.1.0.0"; \ + IC_PATH="2326100"; \ + BASE_URL="https://download.oracle.com/otn_software/linux/instantclient/${IC_PATH}"; \ + BASIC="instantclient-basic-linux.arm64-${IC_VERSION}.zip"; \ + SDK="instantclient-sdk-linux.arm64-${IC_VERSION}.zip"; \ + SQLPLUS="instantclient-sqlplus-linux.arm64-${IC_VERSION}.zip"; \ + else \ + BASE_URL="https://download.oracle.com/otn_software/linux/instantclient"; \ + BASIC="instantclient-basic-linuxx64.zip"; \ + SDK="instantclient-sdk-linuxx64.zip"; \ + SQLPLUS="instantclient-sqlplus-linuxx64.zip"; \ + fi && \ + wget -q "${BASE_URL}/${BASIC}" \ + && wget -q "${BASE_URL}/${SDK}" \ + && wget -q "${BASE_URL}/${SQLPLUS}" \ + && unzip -qo "${BASIC}" \ + && unzip -qo "${SDK}" \ + && unzip -qo "${SQLPLUS}" \ + && mv instantclient_*/ /opt/oracle/instantclient \ + && rm -f instantclient-*.zip + +# Set Oracle environment variables +ENV ORACLE_HOME=/opt/oracle/instantclient \ + LD_LIBRARY_PATH=/opt/oracle/instantclient:$LD_LIBRARY_PATH \ + PATH=/opt/oracle/instantclient:$PATH + +# Switch to vscode user +USER vscode + +# Set working directory +WORKDIR /workspaces/ruby-plsql diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..b845dee --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,31 @@ +{ + "name": "Ruby PL/SQL", + "dockerComposeFile": "docker-compose.yml", + "service": "app", + "workspaceFolder": "/workspaces/ruby-plsql", + "features": { + "ghcr.io/rails/devcontainer/features/ruby:2": { + "version": "4.0.3" + } + }, + "customizations": { + "vscode": { + "extensions": [ + "shopify.ruby-lsp" + ] + } + }, + "initializeCommand": "docker pull gvenzl/oracle-free:latest", + "postCreateCommand": "bash .devcontainer/postCreateCommand.sh", + "remoteEnv": { + "DATABASE_NAME": "FREEPDB1", + "DATABASE_SYS_PASSWORD": "Oracle18", + "NLS_LANG": "American_America.AL32UTF8", + "TNS_ADMIN": "/workspaces/ruby-plsql/ci/network/admin", + "TWO_TASK": "FREEPDB1" + }, + "remoteUser": "vscode", + "mounts": [ + "source=${localWorkspaceFolder}/.bundle,target=/home/vscode/.bundle,type=bind,consistency=delegated" + ] +} diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml new file mode 100644 index 0000000..ae47d9a --- /dev/null +++ b/.devcontainer/docker-compose.yml @@ -0,0 +1,27 @@ +services: + app: + build: + context: . + dockerfile: Dockerfile + volumes: + - ../..:/workspaces:cached + command: sleep infinity + network_mode: service:oracle + + oracle: + image: gvenzl/oracle-free:latest + ports: + - "1521:1521" + environment: + TZ: Europe/Riga + ORACLE_PASSWORD: Oracle18 + healthcheck: + test: ["CMD", "healthcheck.sh"] + interval: 10s + timeout: 5s + retries: 10 + volumes: + - oracle-data:/opt/oracle/oradata + +volumes: + oracle-data: diff --git a/.devcontainer/postCreateCommand.sh b/.devcontainer/postCreateCommand.sh new file mode 100755 index 0000000..22c8c4b --- /dev/null +++ b/.devcontainer/postCreateCommand.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -e + +gem update --system +bundle install + +echo "Waiting for Oracle to be ready..." +oracle_ready=false +for i in $(seq 1 30); do + if echo "exit" | sqlplus -s "system/${DATABASE_SYS_PASSWORD}@${DATABASE_NAME}" > /dev/null 2>&1; then + oracle_ready=true + break + fi + echo "Attempt $i/30 failed, retrying in 10s..." + sleep 10 +done +if [ "$oracle_ready" != "true" ]; then + echo "Oracle did not become ready after 30 attempts; aborting container setup." >&2 + exit 1 +fi + +ci/setup_accounts.sh + +echo "Dev container setup complete. You are ready to start developing ruby-plsql!" diff --git a/.github/workflows/devcontainer.yml b/.github/workflows/devcontainer.yml new file mode 100644 index 0000000..0f3daeb --- /dev/null +++ b/.github/workflows/devcontainer.yml @@ -0,0 +1,28 @@ +name: devcontainer + +on: + schedule: + - cron: "0 6 1 * *" # 1st of every month at 6:00 AM UTC + workflow_dispatch: + +env: + RUBYOPT: "-w" + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Ensure .bundle mount source exists + run: mkdir -p .bundle + + - name: Boot dev container and run tests + uses: devcontainers/ci@v0.3 + with: + runCmd: | + uname -a + lsb_release -a + ruby --version + bundle exec rake spec + bundle exec rubocop