Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
name: CI

on:
push:
branches: [main]
pull_request:

permissions:
contents: read

jobs:
build:
name: Ruby ${{ matrix.ruby }} — install + rubocop + rspec + smoke
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
# Matches gemspec `required_ruby_version >= 3.0`. 3.0 (EOL 2024-03-31)
# is kept here so we catch regressions for consumers still on it;
# 3.2 is the current LTS-ish; 3.4 is latest stable.
ruby: ['3.0', '3.2', '3.4']
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Ruby ${{ matrix.ruby }}
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true # runs `bundle install` and caches gems

- name: Gemfile.lock resolves rubygems.org only (block private-registry drift)
run: |
if [ -f Gemfile.lock ]; then
# Only inspect HTTP(S) remotes; skip the PATH section's `remote: .`
# that bundler writes for the local gemspec source.
bad=$(grep -E '^\s*remote:\s*https?://' Gemfile.lock | grep -vE 'https?://rubygems\.org/?' || true)
if [ -n "$bad" ]; then
echo "::error::Gemfile.lock references a non-rubygems.org source. Re-generate with Gemfile pointing at 'https://rubygems.org' only."
echo "$bad" | head -5
exit 1
fi
fi

- name: RuboCop
run: bundle exec rubocop --parallel

- name: RSpec (with SimpleCov gates)
run: bundle exec rspec

# Smoke: require the top-level module and construct a Client. Catches
# broken require chain / missing constant / class-name typo BEFORE any
# consumer sees them. Analogous to JS `dist smoke` step.
- name: Load smoke (require + Client.new)
run: |
# `-rbundler/setup` MUST come before `-rbybit` — Ruby processes -r
# options in order, and `bybit` transitively requires `faraday`
# (a bundled gem) at load time. On Ruby 3.0, relying on
# `bundle exec`'s injected RUBYOPT alone left faraday unactivated.
bundle exec ruby -rbundler/setup -Ilib -rbybit -e '
raise "Bybit::VERSION missing" unless defined?(Bybit::VERSION)
client = Bybit::Client.new(testnet: true)
raise "Client init failed" unless client.is_a?(Bybit::Client)
puts "smoke OK: bybit-connector-ruby #{Bybit::VERSION} on Ruby #{RUBY_VERSION}"
'

audit:
name: bundler-audit (CVE scan on runtime deps)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true

- name: Install bundler-audit
run: gem install bundler-audit --no-document

# `--update` refreshes the ruby-advisory-db before scanning. Fails on
# any advisory with severity >= high (ignore lower-severity noise with
# --ignore-severity-below high once bundler-audit ships that flag; for
# now we take the default 'fail on any').
- name: Audit Gemfile.lock
run: bundle audit check --update

secret-scan:
name: gitleaks (CLI)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0

# Run the gitleaks binary directly. The `gitleaks/gitleaks-action@v2`
# wrapper requires a paid license for organizations and pins a deprecated
# node20 runtime — the underlying MIT-licensed CLI has neither restriction.
- name: Install gitleaks
env:
GITLEAKS_VERSION: '8.21.2'
run: |
set -eu
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
| tar -xz -C /usr/local/bin gitleaks
gitleaks version

- name: Scan for secrets
run: gitleaks detect --source . --redact --verbose --exit-code 1 --no-banner

pack-check:
name: gem build --dry-run (LICENSE / README / lib present, no spec leak)
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true

- name: Build .gem
run: gem build bybit-connector-ruby.gemspec

- name: Verify tarball contents
run: |
set -eu
GEM=$(ls bybit-connector-ruby-*.gem | head -1)
echo "Inspecting $GEM"
# `gem contents` after install lists files, but we haven't installed
# yet; extract the data tarball inside the .gem and list files.
mkdir -p /tmp/gem-extract && cd /tmp/gem-extract
tar -xf "$GITHUB_WORKSPACE/$GEM"
# data.tar.gz holds the packed files, metadata.gz holds the spec.
tar -tzf data.tar.gz | sort > /tmp/gem-files.txt
echo "--- packed files ---"
cat /tmp/gem-files.txt

# Required entries
for need in LICENSE README.md CHANGELOG.md lib/bybit.rb lib/bybit/version.rb lib/bybit/session.rb lib/bybit/rest_api/base_service.rb; do
if ! grep -qx "$need" /tmp/gem-files.txt; then
echo "::error::Missing from gem tarball: $need"
exit 1
fi
done

# Files that must NEVER ship in the gem
for bad in spec Gemfile Gemfile.lock Rakefile .rubocop.yml .rspec .yardopts; do
if grep -q "^${bad}\(/\|$\)" /tmp/gem-files.txt; then
echo "::error::Gem tarball contains unexpected entry: $bad"
exit 1
fi
done
echo "pack check OK"
77 changes: 77 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Publish to rubygems

on:
push:
tags:
- '[0-9]+.[0-9]+.[0-9]+'

jobs:
publish:
runs-on: ubuntu-latest
# id-token: write enables rubygems.org OIDC trusted publishing if the gem
# is configured for it (Aug-2024 feature). Falls back to RUBYGEMS_API_KEY
# when a trusted-publisher config isn't set up — see the "Publish" step.
permissions:
contents: read
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true

- name: Validate tag is on main branch
run: |
git fetch origin main --depth=50
if ! git branch -r --contains "$GITHUB_SHA" | grep -q 'origin/main'; then
echo "Error: tag '$GITHUB_REF_NAME' is not reachable from origin/main"
exit 1
fi
echo "Branch check passed: tag is on main"

- name: Validate tag matches lib/bybit/version.rb VERSION
run: |
set -eu
GEM_VERSION=$(bundle exec ruby -Ilib -rbybit/version -e 'print Bybit::VERSION')
TAG_VERSION="${GITHUB_REF_NAME}"
if [ "$GEM_VERSION" != "$TAG_VERSION" ]; then
echo "Error: tag '$TAG_VERSION' does not match Bybit::VERSION '$GEM_VERSION'"
exit 1
fi
echo "Version check passed: $TAG_VERSION"

- name: RuboCop + RSpec must pass before publish
run: |
bundle exec rubocop --parallel
bundle exec rspec

- name: Build .gem
run: gem build bybit-connector-ruby.gemspec

# Trusted publishing (OIDC) — no long-lived token required. If the gem
# is NOT yet configured for trusted publishing on rubygems.org, set
# RUBYGEMS_API_KEY as a repo secret and this step will fall back to it.
- name: Publish to rubygems
env:
RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
run: |
set -eu
GEM=$(ls bybit-connector-ruby-*.gem | head -1)
if command -v rubygems-await >/dev/null 2>&1; then :; fi
if [ -n "${RUBYGEMS_API_KEY:-}" ]; then
echo "Using RUBYGEMS_API_KEY auth"
mkdir -p ~/.gem
echo -e ":rubygems_api_key: ${RUBYGEMS_API_KEY}" > ~/.gem/credentials
chmod 600 ~/.gem/credentials
gem push "$GEM"
else
echo "No RUBYGEMS_API_KEY; assuming OIDC trusted publisher is configured on rubygems.org"
gem exec rubygems-trusted-publishing --push "$GEM" || {
echo "::error::Neither RUBYGEMS_API_KEY nor a trusted-publisher config found. Set one of them."
exit 1
}
fi
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/.bundle/
/vendor/bundle/
/vendor/
/coverage/
/pkg/
/doc/
/tmp/
/log/
Gemfile.lock
*.gem
.DS_Store
.env
.env.local
.idea/
.vscode/
3 changes: 3 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--require spec_helper
--format documentation
--color
32 changes: 32 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
AllCops:
TargetRubyVersion: 3.0
NewCops: disable
SuggestExtensions: false
Exclude:
- 'vendor/**/*'
- 'spec/**/*'
- 'examples/**/*'
- 'bin/**/*'
Style/Documentation:
Enabled: false
# Codegen (lib/bybit/rest_api/*_service.rb) emits long YARD @option lines and
# wide method signatures verbatim from the OpenAPI spec — reformatting would
# just be reverted by the next codegen run.
Layout/LineLength:
Max: 400
Metrics/MethodLength:
Max: 40
Metrics/ClassLength:
Max: 2500
Metrics/AbcSize:
Max: 40
Metrics/CyclomaticComplexity:
Max: 12
Metrics/PerceivedComplexity:
Max: 12
Metrics/ParameterLists:
Max: 15
Metrics/BlockLength:
Enabled: false
Naming/AccessorMethodName:
Enabled: false
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.3.5
6 changes: 6 additions & 0 deletions .yardopts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
--markup markdown
--protected
--no-private
-
CHANGELOG.md
LICENSE
Loading
Loading