diff --git a/.eslintignore b/.eslintignore index 1521c8b7..2ebad6ca 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ dist +tmp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..b17295ae --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,468 @@ +# DO NOT EDIT +# This is a generated file by the `rake build_matrix:github:generate` task. +# See `build_matrix.yml` for the build matrix. +# Generate this file with `rake build_matrix:github:generate`. +--- +name: Node.js package CI +'on': + push: + branches: + - main + - develop + pull_request: + types: + - opened + - reopened + - synchronize + schedule: + - cron: 0 0 * * 1-5 +concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: "${{ !contains(github.ref, 'main')}}" +env: + RUNNING_IN_CI: 'true' + NODE_ENV: test +jobs: + validation: + name: Validation + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Validate CI setup + run: rake build_matrix:github:validate + lint-git: + name: Git linter (Lintje) + needs: validation + runs-on: ubuntu-latest + if: "${{ github.event_name != 'schedule' }}" + steps: + - name: Checkout project + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Run Git linter + uses: lintje/action@v0.11 + lint-ruby: + name: Ruby linter (RuboCop) + needs: validation + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Install Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + - name: Run RuboCop + run: bundle exec rubocop + lint-js: + name: JavaScript linter (Prettier) + needs: validation + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: package-lock.json + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap + - name: Node.js Lint (Prettier) + run: npm run lint + integration-tests: + name: Integration tests (${{matrix.name}}) + needs: validation + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: Express + Redis + test-app: express-redis + - name: Express + Knex.js + test-app: express-knex + - name: Koa + MySQL + test-app: koa-mysql + - name: Koa + Mongo + test-app: koa-mongo + - name: Express + Mongoose + test-app: express-mongoose + - name: Express + Postgres + test-app: express-postgres + - name: Express + Apollo + test-app: express-apollo + - name: Express + Yoga + test-app: express-yoga + - name: Express + Prisma + Postgres + test-app: express-prisma-postgres + - name: Express + Prisma + Mongo + test-app: express-prisma-mongo + - name: Next.js + test-app: nextjs + - name: Nest.js + test-app: nestjs + - name: Fastify + test-app: fastify + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Run integration tests + run: script/integration_test_app ${{matrix.test-app}} + build_22: + name: Node.js 22 - Build + runs-on: ubuntu-latest + needs: validation + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: npm + cache-dependency-path: package-lock.json + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap --ci + - name: Build package + run: tmp/mono/bin/mono build + - name: Check install report + run: 'cat ext/install.report; cat ext/install.report | grep ''"status": "success"''' + - name: Save build cache + uses: actions/cache/save@v4 + with: + key: v1-package-build-22-${{github.run_id}} + path: | + build/ + dist/ + ext/ + test_22_unit: + name: Node.js 22 - Tests + needs: build_22 + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: npm + cache-dependency-path: package-lock.json + - name: Restore build cache + uses: actions/cache/restore@v4 + with: + fail-on-cache-miss: true + key: v1-package-build-22-${{github.run_id}} + path: | + build/ + dist/ + ext/ + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap --ci + - name: Run tests + run: tmp/mono/bin/mono test + - name: Run tests for install failure + run: npm run test:failure + test_22_extra_diagnose: + name: Node.js 22 - Extra test - diagnose + needs: build_22 + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + with: + submodules: true + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: npm + cache-dependency-path: package-lock.json + - name: Restore build cache + uses: actions/cache/restore@v4 + with: + fail-on-cache-miss: true + key: v1-package-build-22-${{github.run_id}} + path: | + build/ + dist/ + ext/ + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap --ci + - name: Run tests + run: LANGUAGE=nodejs test/integration/diagnose/bin/test + cleanup_22: + name: Node.js 22 - Clean up + needs: + - test_22_unit + - test_22_extra_diagnose + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Delete build cache + run: gh cache delete v1-package-build-22-${{github.run_id}} + env: + GH_TOKEN: "${{secrets.GITHUB_TOKEN}}" + build_20: + name: Node.js 20 - Build + runs-on: ubuntu-latest + needs: validation + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: npm + cache-dependency-path: package-lock.json + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap --ci + - name: Build package + run: tmp/mono/bin/mono build + - name: Check install report + run: 'cat ext/install.report; cat ext/install.report | grep ''"status": "success"''' + - name: Save build cache + uses: actions/cache/save@v4 + with: + key: v1-package-build-20-${{github.run_id}} + path: | + build/ + dist/ + ext/ + test_20_unit: + name: Node.js 20 - Tests + needs: build_20 + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: npm + cache-dependency-path: package-lock.json + - name: Restore build cache + uses: actions/cache/restore@v4 + with: + fail-on-cache-miss: true + key: v1-package-build-20-${{github.run_id}} + path: | + build/ + dist/ + ext/ + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap --ci + - name: Run tests + run: tmp/mono/bin/mono test + - name: Run tests for install failure + run: npm run test:failure + test_20_extra_diagnose: + name: Node.js 20 - Extra test - diagnose + needs: build_20 + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + with: + submodules: true + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: npm + cache-dependency-path: package-lock.json + - name: Restore build cache + uses: actions/cache/restore@v4 + with: + fail-on-cache-miss: true + key: v1-package-build-20-${{github.run_id}} + path: | + build/ + dist/ + ext/ + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap --ci + - name: Run tests + run: LANGUAGE=nodejs test/integration/diagnose/bin/test + cleanup_20: + name: Node.js 20 - Clean up + needs: + - test_20_unit + - test_20_extra_diagnose + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Delete build cache + run: gh cache delete v1-package-build-20-${{github.run_id}} + env: + GH_TOKEN: "${{secrets.GITHUB_TOKEN}}" + build_18: + name: Node.js 18 - Build + runs-on: ubuntu-latest + needs: validation + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: npm + cache-dependency-path: package-lock.json + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap --ci + - name: Build package + run: tmp/mono/bin/mono build + - name: Check install report + run: 'cat ext/install.report; cat ext/install.report | grep ''"status": "success"''' + - name: Save build cache + uses: actions/cache/save@v4 + with: + key: v1-package-build-18-${{github.run_id}} + path: | + build/ + dist/ + ext/ + test_18_unit: + name: Node.js 18 - Tests + needs: build_18 + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: npm + cache-dependency-path: package-lock.json + - name: Restore build cache + uses: actions/cache/restore@v4 + with: + fail-on-cache-miss: true + key: v1-package-build-18-${{github.run_id}} + path: | + build/ + dist/ + ext/ + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap --ci + - name: Run tests + run: tmp/mono/bin/mono test + - name: Run tests for install failure + run: npm run test:failure + test_18_extra_diagnose: + name: Node.js 18 - Extra test - diagnose + needs: build_18 + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + with: + submodules: true + - name: Checkout Mono + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: tmp/mono + - name: Install Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '3.3' + bundler-cache: true + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + cache: npm + cache-dependency-path: package-lock.json + - name: Restore build cache + uses: actions/cache/restore@v4 + with: + fail-on-cache-miss: true + key: v1-package-build-18-${{github.run_id}} + path: | + build/ + dist/ + ext/ + - name: Install dependencies + run: tmp/mono/bin/mono bootstrap --ci + - name: Run tests + run: LANGUAGE=nodejs test/integration/diagnose/bin/test + cleanup_18: + name: Node.js 18 - Clean up + needs: + - test_18_unit + - test_18_extra_diagnose + runs-on: ubuntu-latest + steps: + - name: Checkout project + uses: actions/checkout@v4 + - name: Delete build cache + run: gh cache delete v1-package-build-18-${{github.run_id}} + env: + GH_TOKEN: "${{secrets.GITHUB_TOKEN}}" diff --git a/.rubocop.yml b/.rubocop.yml index 29c99966..88d725e9 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -6,6 +6,8 @@ AllCops: Exclude: - "node_modules/**/*" - "test/integration/diagnose/**/*" + - "tmp/**/*" + - "vendor/**/*" Style/Documentation: Enabled: false diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml deleted file mode 100644 index 01d4728c..00000000 --- a/.semaphore/semaphore.yml +++ /dev/null @@ -1,249 +0,0 @@ -# DO NOT EDIT -# This is a generated file by the `rake build_matrix:semaphore:generate` task. -# See `build_matrix.yml` for the build matrix. -# Generate this file with `rake build_matrix:semaphore:generate`. ---- -version: v1.0 -name: AppSignal for Node.js -agent: - machine: - type: e1-standard-2 - os_image: ubuntu2004 -auto_cancel: - running: - when: branch != 'main' AND branch != 'develop' -global_job_config: - env_vars: - - name: RUNNING_IN_CI - value: 'true' - - name: NODE_ENV - value: test - - name: _PACKAGE_CACHE - value: v3 - - name: _BUNDLER_CACHE - value: v2 - prologue: - commands: - - checkout - - '[ -n "$NODE_VERSION" ] && sem-version node $NODE_VERSION || echo Skipping Node.js - install' - - npm i -g npm@9.9.2 - - script/setup - - source ~/.bashrc -blocks: -- name: Validation - dependencies: [] - task: - jobs: - - name: Validate CI setup - commands: - - rake build_matrix:semaphore:validate -- name: Linters - dependencies: [] - task: - jobs: - - name: Ruby Lint (RuboCop) - commands: - - cache restore $_BUNDLER_CACHE-bundler-$(checksum Gemfile.lock) - - bundle config set clean 'true' - - bundle config set path .bundle - - bundle install --jobs=3 --retry=3 - - cache store $_BUNDLER_CACHE-bundler-$(checksum Gemfile.lock) .bundle - - bundle exec rubocop - - name: Node.js Lint (Prettier) - env_vars: - - name: NODE_VERSION - value: '18' - commands: - - cache restore - - mono bootstrap --ci - - cache store - - npm run lint - - name: Git Lint (Lintje) - commands: - - script/lint_git -- name: Integration tests - dependencies: - - Validation - task: - jobs: - - name: Express + Redis - commands: - - script/integration_test_app express-redis - - name: Express + Knex.js - commands: - - script/integration_test_app express-knex - - name: Koa + MySQL - commands: - - script/integration_test_app koa-mysql - - name: Koa + Mongo - commands: - - script/integration_test_app koa-mongo - - name: Express + Mongoose - commands: - - script/integration_test_app express-mongoose - - name: Express + Postgres - commands: - - script/integration_test_app express-postgres - - name: Express + Apollo - commands: - - script/integration_test_app express-apollo - - name: Express + Yoga - commands: - - script/integration_test_app express-yoga - - name: Express + Prisma + Postgres - commands: - - script/integration_test_app express-prisma-postgres - - name: Express + Prisma + Mongo - commands: - - script/integration_test_app express-prisma-mongo - - name: Next.js - commands: - - script/integration_test_app nextjs - - name: Nest.js - commands: - - script/integration_test_app nestjs - - name: Fastify - commands: - - script/integration_test_app fastify -- name: Node.js 22 - Build - dependencies: - - Validation - task: - env_vars: - - name: NODE_VERSION - value: '22' - prologue: - commands: - - cache restore - - mono bootstrap --ci - - cache store - jobs: - - name: Build - commands: - - mono build - - cache delete $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache store $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID dist - - cache delete $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache store $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID ext - - cache delete $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache store $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID build - - 'cat ext/install.report; cat ext/install.report | grep ''"status": "success"''' -- name: Node.js 22 - Tests - dependencies: - - Node.js 22 - Build - task: - env_vars: - - name: NODE_VERSION - value: '22' - - name: _APPSIGNAL_EXTENSION_INSTALL - value: 'false' - prologue: - commands: - - cache restore - - cache restore $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache restore $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache restore $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - mono bootstrap --ci - jobs: - - name: Test package - commands: - - mono test - - npm run test:failure - - name: Extra test - diagnose - commands: &1 - - git submodule init - - git submodule update - - LANGUAGE=nodejs test/integration/diagnose/bin/test -- name: Node.js 20 - Build - dependencies: - - Validation - task: - env_vars: - - name: NODE_VERSION - value: '20' - prologue: - commands: - - cache restore - - mono bootstrap --ci - - cache store - jobs: - - name: Build - commands: - - mono build - - cache delete $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache store $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID dist - - cache delete $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache store $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID ext - - cache delete $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache store $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID build - - 'cat ext/install.report; cat ext/install.report | grep ''"status": "success"''' -- name: Node.js 20 - Tests - dependencies: - - Node.js 20 - Build - task: - env_vars: - - name: NODE_VERSION - value: '20' - - name: _APPSIGNAL_EXTENSION_INSTALL - value: 'false' - prologue: - commands: - - cache restore - - cache restore $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache restore $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache restore $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - mono bootstrap --ci - jobs: - - name: Test package - commands: - - mono test - - npm run test:failure - - name: Extra test - diagnose - commands: *1 -- name: Node.js 18 - Build - dependencies: - - Validation - task: - env_vars: - - name: NODE_VERSION - value: '18' - prologue: - commands: - - cache restore - - mono bootstrap --ci - - cache store - jobs: - - name: Build - commands: - - mono build - - cache delete $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache store $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID dist - - cache delete $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache store $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID ext - - cache delete $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache store $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID build - - 'cat ext/install.report; cat ext/install.report | grep ''"status": "success"''' -- name: Node.js 18 - Tests - dependencies: - - Node.js 18 - Build - task: - env_vars: - - name: NODE_VERSION - value: '18' - - name: _APPSIGNAL_EXTENSION_INSTALL - value: 'false' - prologue: - commands: - - cache restore - - cache restore $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache restore $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - cache restore $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID - - mono bootstrap --ci - jobs: - - name: Test package - commands: - - mono test - - npm run test:failure - - name: Extra test - diagnose - commands: *1 diff --git a/Gemfile.lock b/Gemfile.lock index bfd40127..32ac5949 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,33 +2,34 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) - json (2.6.2) - parallel (1.22.1) - parser (3.1.2.1) + json (2.7.2) + language_server-protocol (3.17.0.3) + parallel (1.26.3) + parser (3.3.5.0) ast (~> 2.4.1) + racc + racc (1.8.1) rainbow (3.1.1) - regexp_parser (2.6.0) - rexml (3.3.6) - strscan - rubocop (1.39.0) + regexp_parser (2.9.2) + rubocop (1.66.1) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.1.2.1) + parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 1.8, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.23.0, < 2.0) + regexp_parser (>= 2.4, < 3.0) + rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 3.0) - rubocop-ast (1.23.0) - parser (>= 3.1.1.0) - ruby-progressbar (1.11.0) - strscan (3.1.0) - unicode-display_width (2.3.0) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.32.3) + parser (>= 3.3.1.0) + ruby-progressbar (1.13.0) + unicode-display_width (2.6.0) PLATFORMS aarch64-linux arm64-darwin-21 + arm64-darwin-23 x86_64-darwin-19 x86_64-darwin-20 x86_64-linux diff --git a/README.md b/README.md index d5346c4c..fc2f3a29 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The AppSignal for Node.js library. - [Documentation][docs] - [Support][contact] -![npm (scoped)](https://img.shields.io/npm/v/@appsignal/nodejs) [![Build Status](https://appsignal.semaphoreci.com/badges/appsignal-nodejs/branches/main.svg?style=shields&key=7dd9fe64-f1d5-437b-a5b7-8ac337a26c5b)](https://appsignal.semaphoreci.com/projects/appsignal-nodejs) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) +![npm (scoped)](https://img.shields.io/npm/v/@appsignal/nodejs) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) ## Installation diff --git a/Rakefile b/Rakefile index e37d6c92..cdb0e43f 100644 --- a/Rakefile +++ b/Rakefile @@ -3,149 +3,236 @@ require "set" require "yaml" +CACHE_VERSION = "v1" +CI_WORKFLOW_FILE = ".github/workflows/ci.yml" + namespace :build_matrix do - namespace :semaphore do + namespace :github do task :generate do yaml = YAML.load_file("build_matrix.yml") matrix = yaml["matrix"] - semaphore = yaml["semaphore"] - builds = [] + github = yaml["github"] + jobs = {} matrix["nodejs"].each do |nodejs| nodejs_version = nodejs["nodejs"] - setup = nodejs.fetch("setup", []) - env_vars = nodejs.fetch("env_vars", []) - build_block_name = "Node.js #{nodejs_version} - Build" - build_block = build_semaphore_task( - "name" => build_block_name, - "dependencies" => ["Validation"], - "task" => { - "env_vars" => env_vars + [ - { - "name" => "NODE_VERSION", - "value" => nodejs_version + job_name = "Node.js #{nodejs_version}" + build_job_key = "build_#{nodejs_version}" + build_cache_key = "#{CACHE_VERSION}-package-build-#{nodejs_version}-${{github.run_id}}" + jobs[build_job_key] = { + "name" => "#{job_name} - Build", + "runs-on" => "ubuntu-latest", + "needs" => "validation", + "steps" => [ + { + "name" => "Checkout project", + "uses" => "actions/checkout@v4" + }, + { + "name" => "Checkout Mono", + "uses" => "actions/checkout@v4", + "with" => { + "repository" => "appsignal/mono", + "path" => "tmp/mono" + } + }, + { + "name" => "Install Node.js", + "uses" => "actions/setup-node@v4", + "with" => { + "node-version" => nodejs_version, + "cache" => "npm", + "cache-dependency-path" => "package-lock.json" } - ], - "prologue" => { - "commands" => setup + [ - "cache restore", - "mono bootstrap --ci", - "cache store" - ] - }, - "jobs" => [ - build_semaphore_job( - "name" => "Build", - "commands" => [ - "mono build", - "cache delete $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID", - "cache store $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID dist", - "cache delete $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID", - "cache store $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID ext", - "cache delete $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID", - "cache store $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID build", - "cat ext/install.report; cat ext/install.report | grep '\"status\": \"success\"'" - ] - ) - ] - } - ) - builds << build_block + }, + { + "name" => "Install dependencies", + "run" => "tmp/mono/bin/mono bootstrap --ci" + }, + { + "name" => "Build package", + "run" => "tmp/mono/bin/mono build" + }, + { + "name" => "Check install report", + "run" => + "cat ext/install.report; cat ext/install.report | grep '\"status\": \"success\"'" + }, + { + "name" => "Save build cache", + "uses" => "actions/cache/save@v4", + "with" => { + "key" => build_cache_key, + "path" => <<~PATHS + build/ + dist/ + ext/ + PATHS + } + } + ] + } - primary_block_name = "Node.js #{nodejs_version} - Tests" - primary_jobs = [] - package = matrix["package"] - primary_jobs << build_semaphore_job( - "name" => "Test package", - "commands" => ([ - "mono test" - ] + package.fetch("extra_commands", [])).compact - ) + unit_test_job_key = "test_#{nodejs_version}_unit" + jobs[unit_test_job_key] = { + "name" => "#{job_name} - Tests", + "needs" => build_job_key, + "runs-on" => "ubuntu-latest", + "steps" => [ + { + "name" => "Checkout project", + "uses" => "actions/checkout@v4" + }, + { + "name" => "Checkout Mono", + "uses" => "actions/checkout@v4", + "with" => { + "repository" => "appsignal/mono", + "path" => "tmp/mono" + } + }, + { + "name" => "Install Node.js", + "uses" => "actions/setup-node@v4", + "with" => { + "node-version" => nodejs_version, + "cache" => "npm", + "cache-dependency-path" => "package-lock.json" + } + }, + { + "name" => "Restore build cache", + "uses" => "actions/cache/restore@v4", + "with" => { + "fail-on-cache-miss" => true, + "key" => build_cache_key, + "path" => <<~PATHS + build/ + dist/ + ext/ + PATHS + } + }, + { + "name" => "Install dependencies", + "run" => "tmp/mono/bin/mono bootstrap --ci" + }, + { + "name" => "Run tests", + "run" => "tmp/mono/bin/mono test" + }, + { + "name" => "Run tests for install failure", + "run" => "npm run test:failure" + } + ] + } # Run extra tests against specific package versions. If configured, # run the extra tests configured for the package. - package.fetch("extra_tests", []).each do |test_name, extra_tests| - primary_jobs << build_semaphore_job( - "name" => "Extra test - #{test_name}", - "commands" => extra_tests - ) - end - primary_block = - build_semaphore_task( - "name" => primary_block_name, - "dependencies" => [build_block_name], - "task" => { - "env_vars" => env_vars + [ - { - "name" => "NODE_VERSION", - "value" => nodejs_version - }, - { - "name" => "_APPSIGNAL_EXTENSION_INSTALL", - "value" => "false" - } - ], - "prologue" => { - "commands" => setup + [ - "cache restore", - "cache restore $_PACKAGE_CACHE-dist-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID", - "cache restore $_PACKAGE_CACHE-ext-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID", - "cache restore $_PACKAGE_CACHE-build-v$NODE_VERSION-$SEMAPHORE_WORKFLOW_ID", - "mono bootstrap --ci" - ] - }, - "jobs" => primary_jobs + test_key = "diagnose" + diagnose_job_key = "test_#{nodejs_version}_extra_#{test_key}" + jobs[diagnose_job_key] = { + "name" => "#{job_name} - Extra test - #{test_key}", + "needs" => build_job_key, + "runs-on" => "ubuntu-latest", + "steps" => [ + { + "name" => "Checkout project", + "uses" => "actions/checkout@v4", + "with" => { "submodules" => true } + }, + { + "name" => "Checkout Mono", + "uses" => "actions/checkout@v4", + "with" => { + "repository" => "appsignal/mono", + "path" => "tmp/mono" + } + }, + { + "name" => "Install Ruby", + "uses" => "ruby/setup-ruby@v1", + "with" => { + "ruby-version" => "3.3", + "bundler-cache" => true + } + }, + { + "name" => "Install Node.js", + "uses" => "actions/setup-node@v4", + "with" => { + "node-version" => nodejs_version, + "cache" => "npm", + "cache-dependency-path" => "package-lock.json" + } + }, + { + "name" => "Restore build cache", + "uses" => "actions/cache/restore@v4", + "with" => { + "fail-on-cache-miss" => true, + "key" => build_cache_key, + "path" => <<~PATHS + build/ + dist/ + ext/ + PATHS + } + }, + { + "name" => "Install dependencies", + "run" => "tmp/mono/bin/mono bootstrap --ci" + }, + { + "name" => "Run tests", + "run" => "LANGUAGE=nodejs test/integration/diagnose/bin/test" + } + ] + } + + jobs["cleanup_#{nodejs_version}"] = { + "name" => "#{job_name} - Clean up", + "needs" => [ + unit_test_job_key, + diagnose_job_key + ], + "runs-on" => "ubuntu-latest", + "steps" => [ + { + "name" => "Checkout project", + "uses" => "actions/checkout@v4" + }, + { + "name" => "Delete build cache", + "run" => "gh cache delete #{build_cache_key}", + "env" => { + "GH_TOKEN" => "${{secrets.GITHUB_TOKEN}}" + } } - ) - builds << primary_block + ] + } end - semaphore["blocks"] += builds + github["jobs"].merge!(jobs) header = "# DO NOT EDIT\n" \ - "# This is a generated file by the `rake build_matrix:semaphore:generate` task.\n" \ + "# This is a generated file by the `rake build_matrix:github:generate` task.\n" \ "# See `build_matrix.yml` for the build matrix.\n" \ - "# Generate this file with `rake build_matrix:semaphore:generate`.\n" - generated_yaml = header + YAML.dump(semaphore) - File.write(".semaphore/semaphore.yml", generated_yaml) - puts "Generated `.semaphore/semaphore.yml`" - puts "Task count: #{builds.length}" - puts "Job count: #{builds.sum { |block| block["task"]["jobs"].count }}" + "# Generate this file with `rake build_matrix:github:generate`.\n" + generated_yaml = header + YAML.dump(github) + File.write(CI_WORKFLOW_FILE, generated_yaml) + puts "Generated `#{CI_WORKFLOW_FILE}`" + puts "Job count: #{jobs.length}" end task :validate => :generate do - `git status | grep .semaphore/semaphore.yml 2>&1` + `git status | grep #{CI_WORKFLOW_FILE} 2>&1` if $?.exitstatus.zero? # rubocop:disable Style/SpecialGlobalVars - puts "The `.semaphore/semaphore.yml` is modified. The changes were not committed." - puts "Please run `rake build_matrix:semaphore:generate` and commit the changes." + puts "The `#{CI_WORKFLOW_FILE}` is modified. The changes were not committed." + puts "Please run `rake build_matrix:github:generate` and commit the changes." exit 1 end end end end - -def build_semaphore_task(task_hash) - { - "name" => task_hash.delete("name") { raise "`name` key not found for task" }, - "dependencies" => [], - "task" => task_hash.delete("task") { raise "`task` key not found for task" } - }.merge(task_hash) -end - -def build_semaphore_job(job_hash) - { - "name" => job_hash.delete("name") { "`name` key not found" }, - "commands" => [] - }.merge(job_hash) -end - -def package_has_tests?(package) - test_dir = File.join(package, "src/__tests__") - # Has a dedicated test dir and it contains files - return true if Dir.exist?(test_dir) && Dir.glob(File.join(test_dir, "**", "*.*s")).any? - - Dir - .glob(File.join(package, "**/*.test.*s")) - .reject { |file| file.include?("/node_modules/") } - .any? -end diff --git a/build_matrix.yml b/build_matrix.yml index 9edc95b9..453df2eb 100644 --- a/build_matrix.yml +++ b/build_matrix.yml @@ -1,118 +1,137 @@ -semaphore: # Default `.semaphore/semaphore.yml` contents - version: v1.0 - name: AppSignal for Node.js - agent: - machine: - type: e1-standard-2 - os_image: ubuntu2004 - auto_cancel: - running: - when: branch != 'main' AND branch != 'develop' - global_job_config: - env_vars: - - name: RUNNING_IN_CI - value: 'true' - - name: NODE_ENV - value: test - - name: _PACKAGE_CACHE - value: v3 - - name: _BUNDLER_CACHE - value: v2 - prologue: - commands: - - checkout - - "[ -n \"$NODE_VERSION\" ] && sem-version node $NODE_VERSION || echo Skipping Node.js install" - - npm i -g npm@9.9.2 - - # Mono setup - - script/setup - - source ~/.bashrc - blocks: - - name: Validation - dependencies: [] - task: - jobs: +github: # Default `.github/workflows/ci.yml` contents + name: Node.js package CI + "on": + push: + branches: + - main + - develop + pull_request: + types: + - opened + - reopened + - synchronize + schedule: + - cron: 0 0 * * 1-5 + + concurrency: + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: "${{ !contains(github.ref, 'main')}}" + + env: + RUNNING_IN_CI: "true" + NODE_ENV: test + + jobs: + validation: + name: Validation + runs-on: ubuntu-latest + steps: + - name: "Checkout project" + uses: actions/checkout@v4 + - name: Validate CI setup - commands: - - rake build_matrix:semaphore:validate - - name: Linters - dependencies: [] - task: - jobs: - - name: Ruby Lint (RuboCop) - commands: - - cache restore $_BUNDLER_CACHE-bundler-$(checksum Gemfile.lock) - - bundle config set clean 'true' - - bundle config set path .bundle - - bundle install --jobs=3 --retry=3 - - cache store $_BUNDLER_CACHE-bundler-$(checksum Gemfile.lock) .bundle - - bundle exec rubocop - - name: Node.js Lint (Prettier) - env_vars: - - name: NODE_VERSION - value: "18" - commands: - - cache restore - - mono bootstrap --ci - - cache store - - npm run lint - - name: Git Lint (Lintje) - commands: - - script/lint_git - - name: Integration tests - dependencies: ["Validation"] - task: - jobs: - - name: Express + Redis - commands: - - script/integration_test_app express-redis - - name: Express + Knex.js - commands: - - script/integration_test_app express-knex - - name: Koa + MySQL - commands: - - script/integration_test_app koa-mysql - - name: Koa + Mongo - commands: - - script/integration_test_app koa-mongo - - name: Express + Mongoose - commands: - - script/integration_test_app express-mongoose - - name: Express + Postgres - commands: - - script/integration_test_app express-postgres - - name: Express + Apollo - commands: - - script/integration_test_app express-apollo - - name: Express + Yoga - commands: - - script/integration_test_app express-yoga - - name: Express + Prisma + Postgres - commands: - - script/integration_test_app express-prisma-postgres - - name: Express + Prisma + Mongo - commands: - - script/integration_test_app express-prisma-mongo - - name: Next.js - commands: - - script/integration_test_app nextjs - - name: Nest.js - commands: - - script/integration_test_app nestjs - - name: Fastify - commands: - - script/integration_test_app fastify + run: "rake build_matrix:github:validate" + + lint-git: + name: Git linter (Lintje) + needs: validation + runs-on: ubuntu-latest + if: "${{ github.event_name != 'schedule' }}" + steps: + - name: "Checkout project" + uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Run Git linter + uses: lintje/action@v0.11 + + lint-ruby: + name: Ruby linter (RuboCop) + needs: validation + runs-on: ubuntu-latest + steps: + - name: "Checkout project" + uses: actions/checkout@v4 + + - name: "Install Ruby" + uses: ruby/setup-ruby@v1 + with: + ruby-version: "3.3" + bundler-cache: true + + - name: "Run RuboCop" + run: bundle exec rubocop + + lint-js: + name: JavaScript linter (Prettier) + needs: validation + runs-on: ubuntu-latest + steps: + - name: "Checkout project" + uses: actions/checkout@v4 + + - name: "Checkout Mono" + uses: actions/checkout@v4 + with: + repository: appsignal/mono + path: "tmp/mono" + + - name: "Install Node.js" + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: "npm" + cache-dependency-path: "package-lock.json" + + - name: "Install dependencies" + run: tmp/mono/bin/mono bootstrap + + - name: "Node.js Lint (Prettier)" + run: npm run lint + + integration-tests: + name: Integration tests (${{matrix.name}}) + needs: validation + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - name: Express + Redis + test-app: express-redis + - name: Express + Knex.js + test-app: express-knex + - name: Koa + MySQL + test-app: koa-mysql + - name: Koa + Mongo + test-app: koa-mongo + - name: Express + Mongoose + test-app: express-mongoose + - name: Express + Postgres + test-app: express-postgres + - name: Express + Apollo + test-app: express-apollo + - name: Express + Yoga + test-app: express-yoga + - name: Express + Prisma + Postgres + test-app: express-prisma-postgres + - name: Express + Prisma + Mongo + test-app: express-prisma-mongo + - name: Next.js + test-app: nextjs + - name: Nest.js + test-app: nestjs + - name: Fastify + test-app: fastify + steps: + - name: "Checkout project" + uses: actions/checkout@v4 + + - name: "Run integration tests" + run: script/integration_test_app ${{matrix.test-app}} matrix: nodejs: - nodejs: "22" - nodejs: "20" - nodejs: "18" - package: - extra_commands: # Run in the package's job - - npm run test:failure - extra_tests: # Run as separate jobs for package - diagnose: - - git submodule init - - git submodule update - - LANGUAGE=nodejs test/integration/diagnose/bin/test diff --git a/package-lock.json b/package-lock.json index 5d3244a8..4a2c68bb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@appsignal/nodejs", - "version": "3.4.6", + "version": "3.5.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@appsignal/nodejs", - "version": "3.4.6", + "version": "3.5.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/script/integration_test_app b/script/integration_test_app index 3a4c623c..82314cae 100755 --- a/script/integration_test_app +++ b/script/integration_test_app @@ -20,4 +20,4 @@ if [ ! -d "$test_app_path" ]; then fi cd "$test_app_path" -docker-compose up --abort-on-container-exit --exit-code-from tests --build +docker compose up --abort-on-container-exit --exit-code-from tests --build diff --git a/script/lint_git b/script/lint_git deleted file mode 100755 index 325a5156..00000000 --- a/script/lint_git +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash - -set -eu - -LINTJE_VERSION="0.6.1" - -mkdir -p $HOME/bin -cache_key=v1-lintje-$LINTJE_VERSION -cache restore $cache_key - -# File exists and is executable -if [ -x "$HOME/bin/lintje" ]; then - echo "Restored Lintje $LINTJE_VERSION from cache" -else - echo "Downloading Lintje $LINTJE_VERSION" - curl -L \ - https://github.com/tombruijn/lintje/releases/download/v$LINTJE_VERSION/x86_64-unknown-linux-gnu.tar.gz | \ - tar -xz --directory $HOME/bin - cache store $cache_key $HOME/bin/lintje -fi - -$HOME/bin/lintje $SEMAPHORE_GIT_COMMIT_RANGE diff --git a/script/setup b/script/setup deleted file mode 100755 index f8545c2d..00000000 --- a/script/setup +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash - -set -eu - -# Change version number to a release/tag available on the mono repo to update it -# https://github.com/appsignal/mono/releases -MONO_VERSION="v0.6.0" - -MONO_PATH="$HOME/mono" - -has_cache=false -if which cache >/dev/null; then - has_cache=true -fi - -if $has_cache; then - echo "Restoring mono cache" - cache restore mono-$MONO_VERSION -fi - -# Download mono if the cache restored nothing -if [ ! -d $MONO_PATH ]; then - mkdir -p $MONO_PATH - curl --location https://github.com/appsignal/mono/archive/refs/tags/$MONO_VERSION.tar.gz | \ - tar -xz --strip-components=1 --directory $MONO_PATH - if $has_cache; then - echo "Storing mono cache" - cache store mono-$MONO_VERSION $MONO_PATH - fi -fi - -cd $MONO_PATH -# Install mono always as it adds itself to the PATH -script/setup diff --git a/test/express-prisma-postgres/docker-compose.yml b/test/express-prisma-postgres/docker-compose.yml index 690d36d6..be6b92a7 100644 --- a/test/express-prisma-postgres/docker-compose.yml +++ b/test/express-prisma-postgres/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.8" services: postgres: - image: postgres + image: postgres:16.4 restart: always environment: - POSTGRES_USER=my_user diff --git a/test/helpers/integration_helper.rb b/test/helpers/integration_helper.rb index c70deb32..a505be98 100644 --- a/test/helpers/integration_helper.rb +++ b/test/helpers/integration_helper.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "./config_helper" +require_relative "config_helper" module IntegrationHelper def self.wait_for_start diff --git a/test/helpers/span.rb b/test/helpers/span.rb index 4f4dcece..91c17f15 100644 --- a/test/helpers/span.rb +++ b/test/helpers/span.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require_relative "./config_helper" +require_relative "config_helper" class Span class << self @@ -50,7 +50,7 @@ def root end def root! - raise "There is no root span" if roots.length.zero? + raise "There is no root span" if roots.empty? raise "There is more than one root span" if roots.length > 1 root