Adds Skooma-based OpenAPI validation so SDK-facing request specs can assert that documented request and response contracts match real Rails behavior. This also upgrades the spec to OpenAPI 3.1 and fixes contract drift uncovered while validating core application and platform resources. Closes None Why We want CI to catch OpenAPI drift before it reaches SDK consumers. While wiring validation in, this PR surfaced several mismatches between the documented contract and what the Rails endpoints actually accept or return. What this change does - Adds Skooma-backed OpenAPI validation to the request spec flow and a dedicated OpenAPI validation spec. - Migrates nullable schema definitions to OpenAPI 3.1-compatible unions. - Updates core SDK-facing schemas and payloads across accounts, contacts, conversations, inboxes, messages, teams, reporting events, and platform account resources. - Documents concrete runtime cases that were previously missing or inaccurate, including nested `profile` update payloads, multipart avatar uploads, required profile update bodies, nullable inbox feature flags, and message sender types that include both `Captain::Assistant` and senderless activity-style messages. - Regenerates the committed Swagger JSON and tag-group artifacts used by CI sync checks. Validation - `bundle exec rake swagger:build` - `bundle exec rspec spec/swagger/openapi_spec.rb` --------- Co-authored-by: Sojan Jose <sojan@pepalo.com>
375 lines
12 KiB
YAML
375 lines
12 KiB
YAML
version: 2.1
|
|
orbs:
|
|
node: circleci/node@6.1.0
|
|
qlty-orb: qltysh/qlty-orb@0.0
|
|
|
|
# Shared defaults for setup steps
|
|
defaults: &defaults
|
|
working_directory: ~/build
|
|
machine:
|
|
image: ubuntu-2204:2024.05.1
|
|
resource_class: large
|
|
environment:
|
|
RAILS_LOG_TO_STDOUT: false
|
|
COVERAGE: true
|
|
LOG_LEVEL: warn
|
|
|
|
jobs:
|
|
# Separate job for linting (no parallelism needed)
|
|
lint:
|
|
<<: *defaults
|
|
steps:
|
|
- checkout
|
|
|
|
# Install minimal system dependencies for linting
|
|
- run:
|
|
name: Install System Dependencies
|
|
command: |
|
|
sudo apt-get update
|
|
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \
|
|
libpq-dev \
|
|
build-essential \
|
|
git \
|
|
curl \
|
|
libssl-dev \
|
|
zlib1g-dev \
|
|
libreadline-dev \
|
|
libyaml-dev \
|
|
openjdk-11-jdk \
|
|
jq \
|
|
software-properties-common \
|
|
ca-certificates \
|
|
imagemagick \
|
|
libxml2-dev \
|
|
libxslt1-dev \
|
|
file \
|
|
g++ \
|
|
gcc \
|
|
autoconf \
|
|
gnupg2 \
|
|
patch \
|
|
ruby-dev \
|
|
liblzma-dev \
|
|
libgmp-dev \
|
|
libncurses5-dev \
|
|
libffi-dev \
|
|
libgdbm6 \
|
|
libgdbm-dev \
|
|
libvips
|
|
|
|
- run:
|
|
name: Install RVM and Ruby 3.4.4
|
|
command: |
|
|
sudo apt-get install -y gpg
|
|
gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
|
|
\curl -sSL https://get.rvm.io | bash -s stable
|
|
echo 'source ~/.rvm/scripts/rvm' >> $BASH_ENV
|
|
source ~/.rvm/scripts/rvm
|
|
rvm install "3.4.4"
|
|
rvm use 3.4.4 --default
|
|
gem install bundler -v 2.5.16
|
|
|
|
- run:
|
|
name: Install Application Dependencies
|
|
command: |
|
|
source ~/.rvm/scripts/rvm
|
|
bundle install
|
|
|
|
- node/install:
|
|
node-version: '24.13'
|
|
- node/install-pnpm
|
|
- node/install-packages:
|
|
pkg-manager: pnpm
|
|
override-ci-command: pnpm i
|
|
|
|
# Swagger verification
|
|
- run:
|
|
name: Verify swagger API specification
|
|
command: |
|
|
bundle exec rake swagger:build
|
|
if [[ `git status swagger/swagger.json --porcelain` ]]
|
|
then
|
|
echo "ERROR: The swagger.json file is not in sync with the yaml specification. Run 'rake swagger:build' and commit 'swagger/swagger.json'."
|
|
exit 1
|
|
fi
|
|
mkdir -p ~/tmp
|
|
curl -L https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/7.19.0/openapi-generator-cli-7.19.0.jar > ~/tmp/openapi-generator-cli-7.19.0.jar
|
|
java -jar ~/tmp/openapi-generator-cli-7.19.0.jar validate -i swagger/swagger.json
|
|
|
|
# Bundle audit
|
|
- run:
|
|
name: Bundle audit
|
|
command: bundle exec bundle audit update && bundle exec bundle audit check -v
|
|
|
|
# Rubocop linting
|
|
- run:
|
|
name: Rubocop
|
|
command: bundle exec rubocop --parallel
|
|
|
|
# ESLint linting
|
|
- run:
|
|
name: eslint
|
|
command: pnpm run eslint
|
|
|
|
# Separate job for frontend tests
|
|
frontend-tests:
|
|
<<: *defaults
|
|
steps:
|
|
- checkout
|
|
- node/install:
|
|
node-version: '24.13'
|
|
- node/install-pnpm
|
|
- node/install-packages:
|
|
pkg-manager: pnpm
|
|
override-ci-command: pnpm i
|
|
|
|
- run:
|
|
name: Run frontend tests (with coverage)
|
|
command: pnpm run test:coverage
|
|
|
|
- run:
|
|
name: Move coverage files if they exist
|
|
command: |
|
|
if [ -d "coverage" ]; then
|
|
mkdir -p ~/build/coverage
|
|
cp -r coverage ~/build/coverage/frontend || true
|
|
fi
|
|
when: always
|
|
|
|
- persist_to_workspace:
|
|
root: ~/build
|
|
paths:
|
|
- coverage
|
|
|
|
# Backend tests with parallelization
|
|
backend-tests:
|
|
<<: *defaults
|
|
parallelism: 18
|
|
steps:
|
|
- checkout
|
|
- node/install:
|
|
node-version: '24.13'
|
|
- node/install-pnpm
|
|
- node/install-packages:
|
|
pkg-manager: pnpm
|
|
override-ci-command: pnpm i
|
|
|
|
- run:
|
|
name: Add PostgreSQL repository and update
|
|
command: |
|
|
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
|
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
|
sudo apt-get update -y
|
|
|
|
- run:
|
|
name: Install System Dependencies
|
|
command: |
|
|
sudo apt-get update
|
|
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y \
|
|
libpq-dev \
|
|
redis-server \
|
|
postgresql-common \
|
|
postgresql-16 \
|
|
postgresql-16-pgvector \
|
|
build-essential \
|
|
git \
|
|
curl \
|
|
libssl-dev \
|
|
zlib1g-dev \
|
|
libreadline-dev \
|
|
libyaml-dev \
|
|
openjdk-11-jdk \
|
|
jq \
|
|
software-properties-common \
|
|
ca-certificates \
|
|
imagemagick \
|
|
libxml2-dev \
|
|
libxslt1-dev \
|
|
file \
|
|
g++ \
|
|
gcc \
|
|
autoconf \
|
|
gnupg2 \
|
|
patch \
|
|
ruby-dev \
|
|
liblzma-dev \
|
|
libgmp-dev \
|
|
libncurses5-dev \
|
|
libffi-dev \
|
|
libgdbm6 \
|
|
libgdbm-dev \
|
|
libvips
|
|
|
|
- run:
|
|
name: Install RVM and Ruby 3.4.4
|
|
command: |
|
|
sudo apt-get install -y gpg
|
|
gpg --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
|
|
\curl -sSL https://get.rvm.io | bash -s stable
|
|
echo 'source ~/.rvm/scripts/rvm' >> $BASH_ENV
|
|
source ~/.rvm/scripts/rvm
|
|
rvm install "3.4.4"
|
|
rvm use 3.4.4 --default
|
|
gem install bundler -v 2.5.16
|
|
|
|
- run:
|
|
name: Install Application Dependencies
|
|
command: |
|
|
source ~/.rvm/scripts/rvm
|
|
bundle install
|
|
|
|
# Install and configure OpenSearch
|
|
- run:
|
|
name: Install OpenSearch
|
|
command: |
|
|
# Download and install OpenSearch 2.11.0 (compatible with Elasticsearch 7.x clients)
|
|
wget https://artifacts.opensearch.org/releases/bundle/opensearch/2.11.0/opensearch-2.11.0-linux-x64.tar.gz
|
|
tar -xzf opensearch-2.11.0-linux-x64.tar.gz
|
|
sudo mv opensearch-2.11.0 /opt/opensearch
|
|
|
|
- run:
|
|
name: Configure and Start OpenSearch
|
|
command: |
|
|
# Configure OpenSearch for single-node testing
|
|
cat > /opt/opensearch/config/opensearch.yml \<< EOF
|
|
cluster.name: chatwoot-test
|
|
node.name: node-1
|
|
network.host: 0.0.0.0
|
|
http.port: 9200
|
|
discovery.type: single-node
|
|
plugins.security.disabled: true
|
|
EOF
|
|
|
|
# Set ownership and permissions
|
|
sudo chown -R $USER:$USER /opt/opensearch
|
|
|
|
# Start OpenSearch in background
|
|
/opt/opensearch/bin/opensearch -d -p /tmp/opensearch.pid
|
|
|
|
- run:
|
|
name: Wait for OpenSearch to be ready
|
|
command: |
|
|
echo "Waiting for OpenSearch to start..."
|
|
for i in {1..30}; do
|
|
if curl -s http://localhost:9200/_cluster/health | grep -q '"status"'; then
|
|
echo "OpenSearch is ready!"
|
|
exit 0
|
|
fi
|
|
echo "Waiting... ($i/30)"
|
|
sleep 2
|
|
done
|
|
echo "OpenSearch failed to start"
|
|
exit 1
|
|
|
|
# Configure environment and database
|
|
- run:
|
|
name: Database Setup and Configure Environment Variables
|
|
command: |
|
|
pg_pass=$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 15 ; echo '')
|
|
sed -i "s/REPLACE_WITH_PASSWORD/${pg_pass}/g" ${PWD}/.circleci/setup_chatwoot.sql
|
|
chmod 644 ${PWD}/.circleci/setup_chatwoot.sql
|
|
mv ${PWD}/.circleci/setup_chatwoot.sql /tmp/
|
|
sudo -i -u postgres psql -f /tmp/setup_chatwoot.sql
|
|
cp .env.example .env
|
|
sed -i '/^FRONTEND_URL/d' .env
|
|
sed -i -e '/REDIS_URL/ s/=.*/=redis:\/\/localhost:6379/' .env
|
|
sed -i -e '/POSTGRES_HOST/ s/=.*/=localhost/' .env
|
|
sed -i -e '/POSTGRES_USERNAME/ s/=.*/=chatwoot/' .env
|
|
sed -i -e "/POSTGRES_PASSWORD/ s/=.*/=$pg_pass/" .env
|
|
echo -en "\nINSTALLATION_ENV=circleci" >> ".env"
|
|
echo -en "\nOPENSEARCH_URL=http://localhost:9200" >> ".env"
|
|
|
|
# Database setup
|
|
- run:
|
|
name: Run DB migrations
|
|
command: bundle exec rails db:chatwoot_prepare
|
|
|
|
# Run backend tests (parallelized)
|
|
- run:
|
|
name: Run backend tests
|
|
command: |
|
|
mkdir -p ~/tmp/test-results/rspec
|
|
mkdir -p ~/tmp/test-artifacts
|
|
mkdir -p ~/build/coverage/backend
|
|
|
|
# Use round-robin distribution (same as GitHub Actions) for better test isolation
|
|
# This prevents tests with similar timing from being grouped on the same runner
|
|
SPEC_FILES=($(find spec -name '*_spec.rb' | sort))
|
|
TESTS=""
|
|
|
|
for i in "${!SPEC_FILES[@]}"; do
|
|
if [ $(( i % $CIRCLE_NODE_TOTAL )) -eq $CIRCLE_NODE_INDEX ]; then
|
|
TESTS="$TESTS ${SPEC_FILES[$i]}"
|
|
fi
|
|
done
|
|
|
|
bundle exec rspec -I ./spec --require coverage_helper --require spec_helper --format progress \
|
|
--format RspecJunitFormatter \
|
|
--out ~/tmp/test-results/rspec.xml \
|
|
-- $TESTS
|
|
no_output_timeout: 30m
|
|
|
|
# Store test results for better splitting in future runs
|
|
- store_test_results:
|
|
path: ~/tmp/test-results
|
|
|
|
- run:
|
|
name: Move coverage files if they exist
|
|
command: |
|
|
if [ -d "coverage" ]; then
|
|
mkdir -p ~/build/coverage
|
|
cp -r coverage ~/build/coverage/backend || true
|
|
fi
|
|
when: always
|
|
|
|
- persist_to_workspace:
|
|
root: ~/build
|
|
paths:
|
|
- coverage
|
|
|
|
# Collect coverage from all jobs
|
|
coverage:
|
|
<<: *defaults
|
|
steps:
|
|
- checkout
|
|
- attach_workspace:
|
|
at: ~/build
|
|
|
|
# Qlty coverage publish
|
|
- qlty-orb/coverage_publish:
|
|
files: |
|
|
coverage/frontend/lcov.info
|
|
|
|
- run:
|
|
name: List coverage directory contents
|
|
command: |
|
|
ls -R ~/build/coverage || echo "No coverage directory"
|
|
|
|
- store_artifacts:
|
|
path: coverage
|
|
destination: coverage
|
|
|
|
build:
|
|
<<: *defaults
|
|
steps:
|
|
- run:
|
|
name: Legacy build aggregator
|
|
command: |
|
|
echo "All main jobs passed; build job kept only for GitHub required check compatibility."
|
|
|
|
workflows:
|
|
version: 2
|
|
build:
|
|
jobs:
|
|
- lint
|
|
- frontend-tests
|
|
- backend-tests
|
|
- coverage:
|
|
requires:
|
|
- frontend-tests
|
|
- backend-tests
|
|
- build:
|
|
requires:
|
|
- lint
|
|
- coverage
|