diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..355726a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,20 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 99 + assignees: + - "biow0lf" + + - package-ecosystem: "bundler" + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 99 + allow: + - dependency-type: "direct" + - dependency-type: "indirect" + assignees: + - "biow0lf" diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..8873fd4 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,78 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: ["main"] + pull_request: + # The branches below must be a subset of the branches above + branches: ["main"] + schedule: + - cron: "0 0 * * 1" + +permissions: + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: ["ruby"] + # CodeQL supports [ $supported-codeql-languages ] + # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 + with: + egress-policy: audit + + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3 + + # â„šī¸ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # If the Autobuild fails above, remove it and uncomment the following three lines. + # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. + + # - run: | + # echo "Run, Build Application using script" + # ./location_of_script_within_repo/buildscript.sh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..a9dd01b --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,27 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, +# PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 + with: + egress-policy: audit + + - name: 'Checkout Repository' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: 'Dependency Review' + uses: actions/dependency-review-action@68e9887ce6c0bf076e739ad56332b1ee8bc7f88c # v4 diff --git a/.github/workflows/rspec.yml b/.github/workflows/rspec.yml new file mode 100644 index 0000000..481a993 --- /dev/null +++ b/.github/workflows/rspec.yml @@ -0,0 +1,43 @@ + +name: RSpec + +on: + push: + branches: + - main + pull_request: + branches: + - main + schedule: + - cron: "0 21 * * 6" + +permissions: + contents: read + +jobs: + rspec: + runs-on: ubuntu-24.04 + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: + ruby: ["3.4", "4.0", "head"] + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 + with: + egress-policy: audit + + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - run: rm -f Gemfile.lock + - run: rm -f .ruby-version + - name: Set up Ruby + uses: ruby/setup-ruby@09a7688d3b55cf0e976497ff046b70949eeaccfd # v1.288.0 + with: + ruby-version: ${{ matrix.ruby }} + rubygems: latest + bundler: latest + bundler-cache: true + + - run: bundle exec rspec diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 0000000..0ebf4c5 --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,81 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '20 7 * * 2' + push: + branches: ["main"] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + contents: read + actions: read + # To allow GraphQL ListCommits to work + issues: read + pull-requests: read + # To detect SAST tools + checks: read + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@5ef0c079ce82195b2a36a210272d6b661572d83e # v2.14.2 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@9e907b5e64f6b83e7804b09294d44122997950d6 # v4.32.3 + with: + sarif_file: results.sarif diff --git a/.gitignore b/.gitignore index f294155..56dd36c 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ .bundle .config .yardoc -Gemfile.lock InstalledFiles _yardoc coverage @@ -18,3 +17,5 @@ tmp *.swp .rspec vendor/bundle +.rspec_status +.idea diff --git a/.rspec b/.rspec index b83d9b7..6c6110e 100644 --- a/.rspec +++ b/.rspec @@ -1,3 +1,4 @@ ---color --format documentation +--color --require spec_helper +--order random diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..3eb7161 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,369 @@ +plugins: + - rubocop-performance + - rubocop-rake + - rubocop-rspec + - rubocop-disable_syntax + +AllCops: + TargetRubyVersion: 3.4 + NewCops: enable + +# We use standard as a linter and formatter instead Rubocop. +# Also, we are explicitly disable all rubocop rules what +# already enabled in standard. And standard-performance. + +# Standard rules. Style: + +# Enforced by standard. Disable. +Style/StringLiterals: + Enabled: false + +# Enforced by standard. Disable. +Style/HashSyntax: + Enabled: false + +# Enforced by standard. Disable. +Style/NestedParenthesizedCalls: + Enabled: false + +# Enforced by standard. Disable. +Style/RedundantRegexpArgument: + Enabled: false + +# Enforced by standard. Disable. +Style/PercentLiteralDelimiters: + Enabled: false + +# Enforced by standard. Disable. +Style/RedundantBegin: + Enabled: false + +# Enforced by standard. Disable. +Style/SuperWithArgsParentheses: + Enabled: false + +# Enforced by standard. Disable. +Style/Encoding: + Enabled: false + +# Enforced by standard. Disable. +Style/NumericLiteralPrefix: + Enabled: false + +# Enforced by standard. Disable. +Style/RedundantParentheses: + Enabled: false + +# Enforced by standard. Disable. +Style/EmptyMethod: + Enabled: false + +# Enforced by standard. Disable. +Style/SingleLineMethods: + Enabled: false + +# Enforced by standard. Disable. +Style/SafeNavigation: + Enabled: false + +# Enforced by standard. Disable. +Style/RescueStandardError: + Enabled: false + +# Enforced by standard. Disable. +Style/RedundantSelf: + Enabled: false + +# Enforced by standard. Disable. +Style/TernaryParentheses: + Enabled: false + +# Enforced by standard. Disable. +Style/RedundantLineContinuation: + Enabled: false + +# Enforced by standard. Disable. +Style/SlicingWithRange: + Enabled: false + +# Enforced by standard. Disable. +Style/MultilineIfModifier: + Enabled: false + +# Enforced by standard. Disable. +Style/RedundantCondition: + Enabled: false + +# Enforced by standard. Disable. +Style/RedundantInterpolation: + Enabled: false + +# Enforced by standard. Disable. +Style/OrAssignment: + Enabled: false + +# Enforced by standard. Disable. +Style/ConditionalAssignment: + Enabled: false + +# Enforced by standard. Disable. +Style/ItAssignment: + Enabled: false + +# Enforced by standard. Disable. +Style/EachWithObject: + Enabled: false + +# Enforced by standard. Disable. +Style/GlobalStdStream: + Enabled: false + +# Enforced by standard. Disable. +Style/StringLiteralsInInterpolation: + Enabled: false + +# Disabled as in standard. +Style/HashAsLastArrayItem: + Enabled: false + +# Enforced by standard. Disable. +Style/Alias: + Enabled: false + +# Standard rules. Layout: + +# Enforced by standard. Disable. +Layout/HashAlignment: + Enabled: false + +# Enforced by standard. Disable. +Layout/FirstArrayElementIndentation: + Enabled: false + +# Enforced by standard. Disable. +Layout/SpaceInsideHashLiteralBraces: + Enabled: false + +# Enforced by standard. Disable. +Layout/SpaceInsideStringInterpolation: + Enabled: false + +# Enforced by standard. Disable. +Layout/DotPosition: + Enabled: false + +# Enforced by standard. Disable. +Layout/ExtraSpacing: + Enabled: false + +# Enforced by standard. Disable. +Layout/ArgumentAlignment: + Enabled: false + +# Enforced by standard. Disable. +Layout/MultilineMethodCallBraceLayout: + Enabled: false + +# Enforced by standard. Disable. +Layout/AccessModifierIndentation: + Enabled: false + +# Enforced by standard. Disable. +Layout/FirstHashElementIndentation: + Enabled: false + +# Enforced by standard. Disable. +Layout/IndentationWidth: + Enabled: false + +# Enforced by standard. Disable. +Layout/ElseAlignment: + Enabled: false + +# Enforced by standard. Disable. +Layout/EndAlignment: + Enabled: false + +# Enforced by standard. Disable. +Layout/MultilineHashBraceLayout: + Enabled: false + +# Enforced by standard. Disable. +Layout/EmptyLineBetweenDefs: + Enabled: false + +# Enforced by standard. Disable. +Layout/MultilineArrayBraceLayout: + Enabled: false + +# Enforced by standard. Disable. +Layout/EmptyLineAfterMagicComment: + Enabled: false + +# Enforced by standard. Disable. +Layout/SpaceAroundOperators: + Enabled: false + +# Enforced by standard. Disable. +Layout/ArrayAlignment: + Enabled: false + +# Enforced by standard. Disable. +Layout/AssignmentIndentation: + Enabled: false + +# Enforced by standard. Disable. +Layout/ClosingParenthesisIndentation: + Enabled: false + +# Enforced by standard. Disable. +Layout/LineLength: + Enabled: false + +# Enforced by standard. Disable. +Layout/MultilineMethodCallIndentation: + Enabled: false + +# Enforced by standard. Disable. +Layout/CaseIndentation: + Enabled: false + +# Standard rules. Lint: + +# Enforced by standard. Disable. +Lint/ImplicitStringConcatenation: + Enabled: false + +# Enforced by standard. Disable. +Lint/TripleQuotes: + Enabled: false + +# Enforced by standard. Disable. +Lint/IneffectiveAccessModifier: + Enabled: false + +# Enforced by standard. Disable. +Lint/SymbolConversion: + Enabled: false + +# Enforced by rubocop and standard +Lint/CopDirectiveSyntax: + Enabled: true + +# Enforced by standard. Disable. +Lint/DuplicateMethods: + Enabled: false + +# Enforced by standard. Disable. +Lint/ConstantDefinitionInBlock: + Enabled: false + +# Enforced by standard. Disable. +Lint/UselessTimes: + Enabled: false + +# Standard-performance rules. + +# Enforced by standard-performance. Disable. +Performance/Detect: + Enabled: false + +# Enforced by standard-performance. Disable. +Performance/StringIdentifierArgument: + Enabled: false + +# Enforced by standard-performance. Disable. +Performance/RegexpMatch: + Enabled: false + +# Always enable rubocop Security: + +# Enforced by rubocop and standard +Security/JSONLoad: + Enabled: true + +# Our rubocop rules + +# Bundler rules. + +Bundler/OrderedGems: + Enabled: false + +# Gemspec rules + +Gemspec/OrderedDependencies: + Enabled: false + +# Style rules + +# Don't allow %i[foo bar baz] +Style/SymbolArray: + Enabled: true + EnforcedStyle: brackets + +# Don't allow %w[foo bar baz] +Style/WordArray: + Enabled: true + EnforcedStyle: brackets + +# Disable warnings like "Missing top-level documentation comment for" +Style/Documentation: + Enabled: false + +# Disable as in standard. +Style/ArgumentsForwarding: + Enabled: false + +# RSpec rules + +# Prefer eq over be. +RSpec/BeEq: + Enabled: false + +# Prefer eq over eql. +RSpec/BeEql: + Enabled: false + +# We prefer to use `expect` over `allow`. +RSpec/StubbedMock: + Enabled: false + +# We prefer multiple before blocks in tests. +RSpec/ScatteredSetup: + Enabled: false + +# We use `expect` in before hooks. +RSpec/ExpectInHook: + Enabled: false + +# We use item_1, item_2, etc. Disable. +RSpec/IndexedLet: + Enabled: false + +# We don't use named subject's +RSpec/NamedSubject: + Enabled: false + +# We prefer `receive` over `have_received` +RSpec/MessageSpies: + Enabled: false + +# Naming rules: + +# Disable anonymous block forwarding. +Naming/BlockForwarding: + Enabled: true + EnforcedStyle: explicit + +# Enable and exclude specific files. +Naming/FileName: + Enabled: true + +# Disabled syntax: + +# Disable shorthand hash syntax like: ({ x:, y: }) +# Disable % style literals +Style/DisableSyntax: + DisableSyntax: + - shorthand_hash_syntax + - percent_literals diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..1454f6e --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +4.0.1 diff --git a/.standard.yml b/.standard.yml new file mode 100644 index 0000000..65c3e05 --- /dev/null +++ b/.standard.yml @@ -0,0 +1 @@ +ruby_version: 3.4 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index bfe7a09..0000000 --- a/.travis.yml +++ /dev/null @@ -1,9 +0,0 @@ -language: ruby -rvm: - - 2.1 - - 2.2 - - 2.3.0 -sudo: false -cache: bundler -script: - - bundle exec rspec diff --git a/Gemfile b/Gemfile index 5ee986f..6ab950e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,15 @@ -source 'https://rubygems.org' +# frozen_string_literal: true + +source "https://rubygems.org" -# Specify your gem's dependencies in errbit_github_plugin.gemspec gemspec -gem 'errbit_plugin' -gem 'rspec' -gem 'guard' -gem 'guard-rspec' -gem 'coveralls', :require => false +gem "rake" +gem "rspec" +gem "simplecov", require: false +gem "standard", "1.54.0", require: false +gem "rubocop", require: false +gem "rubocop-disable_syntax", require: false +gem "rubocop-performance", require: false +gem "rubocop-rake", require: false +gem "rubocop-rspec", require: false diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..cc9d4c7 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,221 @@ +PATH + remote: . + specs: + errbit_github_plugin (0.4.0) + activesupport + errbit_plugin + faraday-retry + octokit + +GEM + remote: https://rubygems.org/ + specs: + activesupport (8.1.2) + base64 + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb + i18n (>= 1.6, < 2) + json + logger (>= 1.4.2) + minitest (>= 5.1) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) + uri (>= 0.13.1) + addressable (2.8.8) + public_suffix (>= 2.0.2, < 8.0) + ast (2.4.3) + base64 (0.3.0) + bigdecimal (4.0.1) + concurrent-ruby (1.3.6) + connection_pool (3.0.2) + diff-lcs (1.6.2) + docile (1.4.1) + drb (2.2.3) + errbit_plugin (0.7.0) + faraday (2.14.1) + faraday-net_http (>= 2.0, < 3.5) + json + logger + faraday-net_http (3.4.2) + net-http (~> 0.5) + faraday-retry (2.4.0) + faraday (~> 2.0) + i18n (1.14.8) + concurrent-ruby (~> 1.0) + json (2.18.1) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) + minitest (6.0.1) + prism (~> 1.5) + net-http (0.9.1) + uri (>= 0.11.1) + octokit (10.0.0) + faraday (>= 1, < 3) + sawyer (~> 0.9) + parallel (1.27.0) + parser (3.3.10.2) + ast (~> 2.4.1) + racc + prism (1.9.0) + public_suffix (7.0.2) + racc (1.8.1) + rainbow (3.1.1) + rake (13.3.1) + regexp_parser (2.11.3) + rspec (3.13.2) + rspec-core (~> 3.13.0) + rspec-expectations (~> 3.13.0) + rspec-mocks (~> 3.13.0) + rspec-core (3.13.6) + rspec-support (~> 3.13.0) + rspec-expectations (3.13.5) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-mocks (3.13.7) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.13.0) + rspec-support (3.13.7) + rubocop (1.84.2) + json (~> 2.3) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.49.0, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.49.0) + parser (>= 3.3.7.2) + prism (~> 1.7) + rubocop-disable_syntax (0.2.0) + lint_roller + rubocop (>= 1.72.0) + rubocop-performance (1.26.1) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.47.1, < 2.0) + rubocop-rake (0.7.1) + lint_roller (~> 1.1) + rubocop (>= 1.72.1) + rubocop-rspec (3.9.0) + lint_roller (~> 1.1) + rubocop (~> 1.81) + ruby-progressbar (1.13.0) + sawyer (0.9.3) + addressable (>= 2.3.5) + faraday (>= 0.17.3, < 3) + securerandom (0.4.1) + simplecov (0.22.0) + docile (~> 1.1) + simplecov-html (~> 0.11) + simplecov_json_formatter (~> 0.1) + simplecov-html (0.13.2) + simplecov_json_formatter (0.1.4) + standard (1.54.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.84.0) + standard-custom (~> 1.0.0) + standard-performance (~> 1.8) + standard-custom (1.0.2) + lint_roller (~> 1.0) + rubocop (~> 1.50) + standard-performance (1.9.0) + lint_roller (~> 1.1) + rubocop-performance (~> 1.26.0) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) + unicode-display_width (3.2.0) + unicode-emoji (~> 4.1) + unicode-emoji (4.2.0) + uri (1.1.1) + +PLATFORMS + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl + arm64-darwin + ruby + x86-linux-gnu + x86-linux-musl + x86_64-darwin + x86_64-linux-gnu + x86_64-linux-musl + +DEPENDENCIES + errbit_github_plugin! + rake + rspec + rubocop + rubocop-disable_syntax + rubocop-performance + rubocop-rake + rubocop-rspec + simplecov + standard (= 1.54.0) + +CHECKSUMS + activesupport (8.1.2) sha256=88842578ccd0d40f658289b0e8c842acfe9af751afee2e0744a7873f50b6fdae + addressable (2.8.8) sha256=7c13b8f9536cf6364c03b9d417c19986019e28f7c00ac8132da4eb0fe393b057 + ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 + base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b + bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7 + concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab + connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a + diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962 + docile (1.4.1) sha256=96159be799bfa73cdb721b840e9802126e4e03dfc26863db73647204c727f21e + drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 + errbit_github_plugin (0.4.0) + errbit_plugin (0.7.0) sha256=2deec87f62dbc953ff86a69b46bb4d339678a9a9439342b85f3dfb909621d94d + faraday (2.14.1) sha256=a43cceedc1e39d188f4d2cdd360a8aaa6a11da0c407052e426ba8d3fb42ef61c + faraday-net_http (3.4.2) sha256=f147758260d3526939bf57ecf911682f94926a3666502e24c69992765875906c + faraday-retry (2.4.0) sha256=7b79c48fb7e56526faf247b12d94a680071ff40c9fda7cf1ec1549439ad11ebe + i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5 + json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986 + language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc + lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 + logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203 + minitest (6.0.1) sha256=7854c74f48e2e975969062833adc4013f249a4b212f5e7b9d5c040bf838d54bb + net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996 + octokit (10.0.0) sha256=82e99a539b7637b7e905e6d277bb0c1a4bed56735935cc33db6da7eae49a24e8 + parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 + parser (3.3.10.2) sha256=6f60c84aa4bdcedb6d1a2434b738fe8a8136807b6adc8f7f53b97da9bc4e9357 + prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 + public_suffix (7.0.2) sha256=9114090c8e4e7135c1fd0e7acfea33afaab38101884320c65aaa0ffb8e26a857 + racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f + rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a + rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c + regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4 + rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587 + rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d + rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 + rspec-mocks (3.13.7) sha256=0979034e64b1d7a838aaaddf12bf065ea4dc40ef3d4c39f01f93ae2c66c62b1c + rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c + rubocop (1.84.2) sha256=5692cea54168f3dc8cb79a6fe95c5424b7ea893c707ad7a4307b0585e88dbf5f + rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd + rubocop-disable_syntax (0.2.0) sha256=1e61645773b3fc2f74e995ec65f605f7db59437c274fdfd4f385f60bf86af73e + rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834 + rubocop-rake (0.7.1) sha256=3797f2b6810c3e9df7376c26d5f44f3475eda59eb1adc38e6f62ecf027cbae4d + rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2 + ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 + sawyer (0.9.3) sha256=0d0f19298408047037638639fe62f4794483fb04320269169bd41af2bdcf5e41 + securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 + simplecov (0.22.0) sha256=fe2622c7834ff23b98066bb0a854284b2729a569ac659f82621fc22ef36213a5 + simplecov-html (0.13.2) sha256=bd0b8e54e7c2d7685927e8d6286466359b6f16b18cb0df47b508e8d73c777246 + simplecov_json_formatter (0.1.4) sha256=529418fbe8de1713ac2b2d612aa3daa56d316975d307244399fa4838c601b428 + standard (1.54.0) sha256=7a4b08f83d9893083c8f03bc486f0feeb6a84d48233b40829c03ef4767ea0100 + standard-custom (1.0.2) sha256=424adc84179a074f1a2a309bb9cf7cd6bfdb2b6541f20c6bf9436c0ba22a652b + standard-performance (1.9.0) sha256=49483d31be448292951d80e5e67cdcb576c2502103c7b40aec6f1b6e9c88e3f2 + tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b + unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42 + unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f + uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6 + +BUNDLED WITH + 4.0.4 diff --git a/README.md b/README.md index f62cdde..66ec046 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,7 @@ -# ErrbitGithubPlugin +# Errbit GitHub Plugin -TODO: Write a gem description +[![RSpec](https://github.com/errbit/errbit_github_plugin/actions/workflows/rspec.yml/badge.svg)](https://github.com/errbit/errbit_github_plugin/actions/workflows/rspec.yml) +[![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/standardrb/standard) -## Installation - -Add this line to your application's Gemfile: - - gem 'errbit_github_plugin' - -And then execute: - - $ bundle - -Or install it yourself as: - - $ gem install errbit_github_plugin - -## Usage - -TODO: Write usage instructions here - -## Contributing - -1. Fork it -2. Create your feature branch (`git checkout -b my-new-feature`) -3. Commit your changes (`git commit -am 'Add some feature'`) -4. Push to the branch (`git push origin my-new-feature`) -5. Create new Pull Request +This plugin provides GitHub issue tracker integration for Errbit, and it is the +only plugin included by default in Errbit. diff --git a/Rakefile b/Rakefile index 2995527..5263b58 100644 --- a/Rakefile +++ b/Rakefile @@ -1 +1,3 @@ +# frozen_string_literal: true + require "bundler/gem_tasks" diff --git a/errbit_github_plugin.gemspec b/errbit_github_plugin.gemspec index 53d31f5..88083e6 100644 --- a/errbit_github_plugin.gemspec +++ b/errbit_github_plugin.gemspec @@ -1,28 +1,37 @@ -# coding: utf-8 -lib = File.expand_path('../lib', __FILE__) -$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) -require 'errbit_github_plugin/version' +# frozen_string_literal: true + +require_relative "lib/errbit_github_plugin/version" Gem::Specification.new do |spec| - spec.name = "errbit_github_plugin" - spec.version = ErrbitGithubPlugin::VERSION - spec.authors = ["Stephen Crosby", "Cyril Mougel"] - spec.email = ["stevecrozz@gmail.com", "cyril.mougel@gmail.com"] + spec.name = "errbit_github_plugin" + spec.version = ErrbitGithubPlugin::VERSION + spec.authors = ["Stephen Crosby"] + spec.email = ["stevecrozz@gmail.com"] + + spec.summary = "GitHub integration for Errbit" + spec.description = "GitHub integration for Errbit" + spec.homepage = "https://github.com/errbit/errbit_github_plugin" + spec.license = "MIT" + spec.required_ruby_version = ">= 3.4.0" - spec.description = %q{GitHub integration for Errbit} - spec.summary = %q{GitHub integration for Errbit} - spec.homepage = "https://github.com/errbit/errbit_github_plugin" - spec.license = "MIT" + spec.metadata["homepage_uri"] = spec.homepage + spec.metadata["source_code_uri"] = spec.homepage + spec.metadata["bug_tracker_uri"] = "#{spec.homepage}/issues" + spec.metadata["rubygems_mfa_required"] = "true" - spec.files = `git ls-files`.split($/) - spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } - spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + # Specify which files should be added to the gem when it is released. + # The `git ls-files -z` loads the files in the RubyGem that have been added into git. + gemspec = File.basename(__FILE__) + spec.files = IO.popen(["git", "ls-files", "-z"], chdir: __dir__, err: IO::NULL) do |ls| + ls.readlines("\x0", chomp: true).reject do |f| + (f == gemspec) || + f.start_with?("bin/", "test/", "spec/", "features/", ".git", ".github", "appveyor", "Gemfile") + end + end spec.require_paths = ["lib"] spec.add_dependency "errbit_plugin" + spec.add_dependency "faraday-retry" spec.add_dependency "octokit" - - spec.add_development_dependency "bundler", "~> 1.3" - spec.add_development_dependency "rake" - spec.add_development_dependency "activesupport" + spec.add_dependency "activesupport" end diff --git a/lib/errbit_github_plugin.rb b/lib/errbit_github_plugin.rb index fcddc1e..57afb5a 100644 --- a/lib/errbit_github_plugin.rb +++ b/lib/errbit_github_plugin.rb @@ -1,14 +1,16 @@ +# frozen_string_literal: true + require "errbit_github_plugin/version" -require 'errbit_github_plugin/error' -require 'errbit_github_plugin/issue_tracker' +require "errbit_github_plugin/authentication_error" +require "errbit_github_plugin/issue_tracker" module ErrbitGithubPlugin def self.root - File.expand_path '../..', __FILE__ + File.expand_path "../..", __FILE__ end def self.read_static_file(file) - File.read(File.join(self.root, 'static', file)) + File.read(File.join(root, "static", file)) end end diff --git a/lib/errbit_github_plugin/error.rb b/lib/errbit_github_plugin/authentication_error.rb similarity index 70% rename from lib/errbit_github_plugin/error.rb rename to lib/errbit_github_plugin/authentication_error.rb index dc8859f..655dbf6 100644 --- a/lib/errbit_github_plugin/error.rb +++ b/lib/errbit_github_plugin/authentication_error.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ErrbitGithubPlugin class AuthenticationError < Exception; end end diff --git a/lib/errbit_github_plugin/issue_tracker.rb b/lib/errbit_github_plugin/issue_tracker.rb index 1329cab..106e1ad 100644 --- a/lib/errbit_github_plugin/issue_tracker.rb +++ b/lib/errbit_github_plugin/issue_tracker.rb @@ -1,15 +1,17 @@ -require 'octokit' +# frozen_string_literal: true + +require "active_support/core_ext/object/blank" +require "octokit" module ErrbitGithubPlugin class IssueTracker < ErrbitPlugin::IssueTracker + LABEL = "github" - LABEL = 'github' - - NOTE = 'Please configure your github repository in the GITHUB ' << - 'REPO field above.
Instead of providing your ' << - 'username & password, you can link your Github account to your ' << - 'user profile, and allow Errbit to create issues using your ' << - 'OAuth token.' + NOTE = "Please configure your GitHub repository in the GITHUB " \ + "REPO field above.
Instead of providing your " \ + "username & password, you can link your GitHub account to your " \ + "user profile, and allow Errbit to create issues using your " \ + "OAuth token." FIELDS = { username: { @@ -35,13 +37,13 @@ def self.fields def self.icons @icons ||= { create: [ - 'image/png', ErrbitGithubPlugin.read_static_file('github_create.png') + "image/png", ErrbitGithubPlugin.read_static_file("github_create.png") ], goto: [ - 'image/png', ErrbitGithubPlugin.read_static_file('github_goto.png'), + "image/png", ErrbitGithubPlugin.read_static_file("github_goto.png") ], inactive: [ - 'image/png', ErrbitGithubPlugin.read_static_file('github_inactive.png'), + "image/png", ErrbitGithubPlugin.read_static_file("github_inactive.png") ] } end @@ -56,12 +58,15 @@ def url def errors errors = [] - if self.class.fields.detect {|f| options[f[0]].blank? } - errors << [:base, 'You must specify your GitHub username and password'] + + if self.class.fields.detect { |f| options[f[0]].blank? } + errors << [:base, "You must specify your GitHub username and password"] end + if repo.blank? - errors << [:base, 'You must specify your GitHub repository url.'] + errors << [:base, "You must specify your GitHub repository url."] end + errors end @@ -70,17 +75,42 @@ def repo end def create_issue(title, body, user: {}) - if user['github_login'] && user['github_oauth_token'] - github_client = Octokit::Client.new( - login: user['github_login'], access_token: user['github_oauth_token']) + github_client = if user["github_login"] && user["github_oauth_token"] + Octokit::Client.new( + login: user["github_login"], access_token: user["github_oauth_token"] + ) else - github_client = Octokit::Client.new( - login: options['username'], password: options['password']) + Octokit::Client.new( + login: options["username"], password: options["password"] + ) end issue = github_client.create_issue(repo, title, body) issue.html_url rescue Octokit::Unauthorized raise ErrbitGithubPlugin::AuthenticationError, "Could not authenticate with GitHub. Please check your username and password." end + + # @param url [String] + # @param user [Hash] + # @return [String] The URL of the closed issue + def close_issue(url, user: {}) + github_client = if user["github_login"] && user["github_oauth_token"] + Octokit::Client.new( + login: user["github_login"], access_token: user["github_oauth_token"] + ) + else + Octokit::Client.new( + login: options["username"], password: options["password"] + ) + end + # It would be better to get the number from issue.number when we create the issue, + # however, since we only have the url, get the number from it. + # ex: "https://github.com/octocat/Hello-World/issues/1347" + issue_number = url.split("/").last + issue = github_client.close_issue(repo, issue_number) + issue.html_url + rescue Octokit::Unauthorized + raise ErrbitGithubPlugin::AuthenticationError, "Could not authenticate with GitHub. Please check your username and password." + end end end diff --git a/lib/errbit_github_plugin/version.rb b/lib/errbit_github_plugin/version.rb index 95c2335..839f65d 100644 --- a/lib/errbit_github_plugin/version.rb +++ b/lib/errbit_github_plugin/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ErrbitGithubPlugin - VERSION = "0.2.0" + VERSION = "0.4.0" end diff --git a/spec/errbit_github_plugin/issue_tracker_spec.rb b/spec/errbit_github_plugin/issue_tracker_spec.rb new file mode 100644 index 0000000..a6a9a88 --- /dev/null +++ b/spec/errbit_github_plugin/issue_tracker_spec.rb @@ -0,0 +1,249 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe ErrbitGithubPlugin::IssueTracker do + describe ".label" do + it { expect(described_class.label).to eq("github") } + end + + describe ".note" do + it { expect(described_class.note).to start_with("Please configure your GitHub") } + end + + describe ".fields" do + it do + expect(described_class.fields).to eq( + { + username: { + placeholder: "Your username on GitHub" + }, + password: { + placeholder: "Password for your account" + } + } + ) + end + end + + describe ".icons" do + it "puts create icon onto the icons" do + expect(described_class.icons[:create][0]).to eq("image/png") + + expect(described_class.icons[:create][1]) + .to eq(ErrbitGithubPlugin.read_static_file("github_create.png")) + end + + it "puts goto icon onto the icons" do + expect(described_class.icons[:goto][0]).to eq("image/png") + + expect(described_class.icons[:goto][1]) + .to eq(ErrbitGithubPlugin.read_static_file("github_goto.png")) + end + + it "puts inactive icon onto the icons" do + expect(described_class.icons[:inactive][0]).to eq("image/png") + + expect(described_class.icons[:inactive][1]) + .to eq(ErrbitGithubPlugin.read_static_file("github_inactive.png")) + end + end + + let(:tracker) { described_class.new(options) } + + describe "#configured?" do + context "with errors" do + let(:options) { {invalid_key: ""} } + + it "return false" do + expect(tracker.configured?).to eq(false) + end + end + + context "without errors" do + let(:options) do + {username: "foo", password: "bar", github_repo: "user/repository"} + end + + it "return true" do + expect(tracker.configured?).to eq(true) + end + end + end + + describe "#url" do + let(:options) { {github_repo: "user/repo"} } + + it "returns issues url" do + expect(tracker.url).to eq("https://github.com/user/repo/issues") + end + end + + describe "#errors" do + subject { tracker.errors } + + context "without username" do + let(:options) { {username: "", password: "bar", github_repo: "repo"} } + + it { is_expected.not_to be_empty } + end + + context "without password" do + let(:options) do + {username: "", password: "bar", github_repo: "repo"} + end + + it { is_expected.not_to be_empty } + end + + context "without github_repo" do + let(:options) do + {username: "foo", password: "bar", github_repo: ""} + end + + it { is_expected.not_to be_empty } + end + + context "with completed options" do + let(:options) do + {username: "foo", password: "bar", github_repo: "repo"} + end + + it { is_expected.to be_empty } + end + end + + describe "#repo" do + let(:options) { {github_repo: "baz"} } + + it "returns github repo" do + expect(tracker.repo).to eq("baz") + end + end + + describe "#create_issue" do + subject { tracker.create_issue("title", "body", user: user) } + + let(:options) do + {username: "foo", password: "bar", github_repo: "user/repos"} + end + + let(:fake_github_client) do + double("Fake GitHub Client").tap do |github_client| + expect(github_client).to receive(:create_issue).and_return(fake_issue) + end + end + + let(:fake_issue) do + double("Fake Issue").tap do |issue| + expect(issue).to receive(:html_url).and_return("http://github.com/user/repos/issues/878").twice + end + end + + context "signed in with token" do + let(:user) do + { + "github_login" => "bob", + "github_oauth_token" => "valid_token" + } + end + + it "return issue url" do + expect(Octokit::Client).to receive(:new).with( + login: user["github_login"], access_token: user["github_oauth_token"] + ).and_return(fake_github_client) + + expect(subject).to eq(fake_issue.html_url) + end + end + + context "signed in with password" do + let(:user) { {} } + + it "return issue url" do + expect(Octokit::Client).to receive(:new).with( + login: options["username"], password: options["password"] + ).and_return(fake_github_client) + + expect(subject).to eq(fake_issue.html_url) + end + end + + context "when unauthentication error" do + let(:user) do + {"github_login" => "alice", "github_oauth_token" => "invalid_token"} + end + + it "raise AuthenticationError" do + expect(Octokit::Client).to receive(:new).with( + login: user["github_login"], access_token: user["github_oauth_token"] + ).and_raise(Octokit::Unauthorized) + + expect { subject }.to raise_error(ErrbitGithubPlugin::AuthenticationError) + end + end + end + + describe "#close_issue" do + subject { tracker.close_issue("url", user: user) } + + let(:options) do + {username: "foo", password: "bar", github_repo: "user/repository"} + end + + let(:fake_github_client) do + double("Fake GitHub Client").tap do |github_client| + expect(github_client).to receive(:close_issue).and_return(fake_issue) + end + end + + let(:fake_issue) do + double("Fake Issue").tap do |issue| + expect(issue).to receive(:html_url).and_return("http://github.com/user/repos/issues/878").twice + end + end + + context "signed in with token" do + let(:user) do + { + "github_login" => "bob", + "github_oauth_token" => "valid_token" + } + end + + it "return issue url" do + expect(Octokit::Client).to receive(:new).with( + login: user["github_login"], access_token: user["github_oauth_token"] + ).and_return(fake_github_client) + + expect(subject).to eq(fake_issue.html_url) + end + end + + context "signed in with password" do + let(:user) { {} } + + it "return issue url" do + expect(Octokit::Client).to receive(:new).with( + login: options["username"], password: options["password"] + ).and_return(fake_github_client) + + expect(subject).to eq(fake_issue.html_url) + end + end + + context "when unauthentication error" do + let(:user) do + {"github_login" => "alice", "github_oauth_token" => "invalid_token"} + end + # + it "raise AuthenticationError" do + expect(Octokit::Client).to receive(:new).with( + login: user["github_login"], access_token: user["github_oauth_token"] + ).and_raise(Octokit::Unauthorized) + + expect { subject }.to raise_error(ErrbitGithubPlugin::AuthenticationError) + end + end + end +end diff --git a/spec/issue_tracker_spec.rb b/spec/issue_tracker_spec.rb deleted file mode 100644 index 22dd275..0000000 --- a/spec/issue_tracker_spec.rb +++ /dev/null @@ -1,156 +0,0 @@ -describe ErrbitGithubPlugin::IssueTracker do - describe '.label' do - it 'return LABEL' do - expect(described_class.label).to eq described_class::LABEL - end - end - - describe '.note' do - it 'return NOTE' do - expect(described_class.note).to eq described_class::NOTE - end - end - - describe '.fields' do - it 'return FIELDS' do - expect(described_class.fields).to eq described_class::FIELDS - end - end - - describe '.icons' do - - it 'puts create icon onto the icons' do - expect(described_class.icons[:create][0]).to eq 'image/png' - expect( - described_class.icons[:create][1] - ).to eq ErrbitGithubPlugin.read_static_file('github_create.png') - end - - it 'puts goto icon onto the icons' do - expect(described_class.icons[:goto][0]).to eq 'image/png' - expect( - described_class.icons[:goto][1] - ).to eq ErrbitGithubPlugin.read_static_file('github_goto.png') - end - - it 'puts inactive icon onto the icons' do - expect(described_class.icons[:inactive][0]).to eq 'image/png' - expect( - described_class.icons[:inactive][1] - ).to eq ErrbitGithubPlugin.read_static_file('github_inactive.png') - end - end - - let(:tracker) { described_class.new(options) } - - describe '#configured?' do - context 'with errors' do - let(:options) { { invalid_key: '' } } - it 'return false' do - expect(tracker.configured?).to eq false - end - end - context 'without errors' do - let(:options) do - { username: 'foo', password: 'bar', github_repo: 'user/repos' } - end - it 'return true' do - expect(tracker.configured?).to eq true - end - end - end - - describe '#url' do - let(:options) { { github_repo: 'repo' } } - it 'returns issues url' do - expect(tracker.url).to eq 'https://github.com/repo/issues' - end - end - - describe '#errors' do - subject { tracker.errors } - context 'without username' do - let(:options) { { username: '', password: 'bar', github_repo: 'repo' } } - it { is_expected.not_to be_empty } - end - context 'without password' do - let(:options) do - { username: '', password: 'bar', github_repo: 'repo' } - end - it { is_expected.not_to be_empty } - end - context 'without github_repo' do - let(:options) do - { username: 'foo', password: 'bar', github_repo: '' } - end - it { is_expected.not_to be_empty } - end - context 'with completed options' do - let(:options) do - { username: 'foo', password: 'bar', github_repo: 'repo' } - end - it { is_expected.to be_empty } - end - end - - describe '#repo' do - let(:options) { { github_repo: 'baz' } } - it 'returns github repo' do - expect(tracker.repo).to eq 'baz' - end - end - - describe '#create_issue' do - subject { tracker.create_issue('title', 'body', user: user) } - let(:options) do - { username: 'foo', password: 'bar', github_repo: 'user/repos' } - end - let(:fake_github_client) do - double('Fake GitHub Client').tap do |github_client| - github_client.stub(:create_issue).and_return(fake_issue) - end - end - let(:fake_issue) do - double('Fake Issue').tap do |issue| - issue.stub(:html_url).and_return('http://github.com/user/repos/issues/878') - end - end - - context 'signed in with token' do - let(:user) do - { - 'github_login' => 'bob', - 'github_oauth_token' => 'valid_token' - } - end - it 'return issue url' do - Octokit::Client.stub(:new).with( - login: user['github_login'], access_token: user['github_oauth_token'] - ).and_return(fake_github_client) - expect(subject).to eq fake_issue.html_url - end - end - - context 'signed in with password' do - let(:user) { {} } - it 'return issue url' do - (Octokit::Client).stub(:new).with( - login: options['username'], password: options['password'] - ).and_return(fake_github_client) - expect(subject).to eq fake_issue.html_url - end - end - - context 'when unauthentication error' do - let(:user) do - { 'github_login' => 'alice', 'github_oauth_token' => 'invalid_token' } - end - it 'raise AuthenticationError' do - (Octokit::Client).stub(:new).with( - login: user['github_login'], access_token: user['github_oauth_token'] - ).and_raise(Octokit::Unauthorized) - expect { subject }.to raise_error - end - end - end -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index badea54..e5b39a9 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,28 +1,26 @@ -if ENV['COVERAGE'] - require 'simplecov' - if ENV['CI'] - require 'coveralls' - Coveralls.wear! - SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[ - SimpleCov::Formatter::HTMLFormatter, - Coveralls::SimpleCov::Formatter - ] - end +# frozen_string_literal: true + +require "simplecov" + +SimpleCov.start do + enable_coverage :branch + + primary_coverage :branch - SimpleCov.start + add_filter "spec/" end -require 'errbit_plugin' -require 'errbit_github_plugin' -require 'active_support/all' +require "errbit_plugin" +require "errbit_github_plugin" RSpec.configure do |config| - config.run_all_when_everything_filtered = true - config.filter_run :focus - - # Run specs in random order to surface order dependencies. If you find an - # order dependency and want to debug it, you can fix the order by providing - # the seed, which is printed after each run. - # --seed 1234 - config.order = 'random' + # Enable flags like --only-failures and --next-failure + config.example_status_persistence_file_path = ".rspec_status" + + # Disable RSpec exposing methods globally on `Module` and `main` + config.disable_monkey_patching! + + config.expect_with :rspec do |c| + c.syntax = :expect + end end diff --git a/vendor/assets/images/github_create.png b/vendor/assets/images/github_create.png deleted file mode 100644 index 8bc8e9d..0000000 Binary files a/vendor/assets/images/github_create.png and /dev/null differ diff --git a/vendor/assets/images/github_inactive.png b/vendor/assets/images/github_inactive.png deleted file mode 100644 index 1bcffc7..0000000 Binary files a/vendor/assets/images/github_inactive.png and /dev/null differ