From fd1a9632e2400f46a5dcb95ee753ede74dcb4069 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Thu, 30 Apr 2026 15:39:13 +0900 Subject: [PATCH 1/3] Add devcontainer environment Adds a .devcontainer/ setup mirroring rsim/oracle-enhanced so contributors can develop ruby-plsql against a containerized Oracle Free database without local Oracle Instant Client installation. The Dockerfile installs Instant Client (latest x86_64, pinned 23.26.1.0.0 on arm64), docker-compose.yml boots gvenzl/oracle-free alongside the app container, and postCreateCommand.sh runs the existing ci/setup_accounts.sh once Oracle is healthy to provision the hr and arunit users used by the spec suite. --- .devcontainer/Dockerfile | 53 ++++++++++++++++++++++++++++++ .devcontainer/devcontainer.json | 31 +++++++++++++++++ .devcontainer/docker-compose.yml | 27 +++++++++++++++ .devcontainer/postCreateCommand.sh | 25 ++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml create mode 100755 .devcontainer/postCreateCommand.sh 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..5d63583 --- /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.2" + } + }, + "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!" From 91d2516d4ff67ff396056a8805c27c298a8a21df Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Thu, 30 Apr 2026 15:39:18 +0900 Subject: [PATCH 2/3] Add devcontainer CI workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Boots the devcontainer on a monthly schedule (and on manual dispatch) to catch regressions in the dev environment — Oracle Instant Client URLs, gvenzl/oracle-free image changes, and base image drift — before they hit contributors. Runs the spec suite and rubocop inside the booted container. --- .github/workflows/devcontainer.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/devcontainer.yml 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 From f63bb08b1b17da0c39c7bcd50582bfc2dec4d6cc Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Thu, 30 Apr 2026 15:43:38 +0900 Subject: [PATCH 3/3] Bump devcontainer Ruby to 4.0.3 Matches the current default of ghcr.io/rails/devcontainer/features/ruby:2 (v2.2.1). --- .devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5d63583..b845dee 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,7 +5,7 @@ "workspaceFolder": "/workspaces/ruby-plsql", "features": { "ghcr.io/rails/devcontainer/features/ruby:2": { - "version": "4.0.2" + "version": "4.0.3" } }, "customizations": {