The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .circleci
    └── config.yml
├── .editorconfig
├── .gitattributes
├── .github
    ├── dependabot.yml
    ├── issue_template.md
    ├── pull_request_template.md
    └── workflows
    │   ├── CI.yml
    │   └── publish_package.yml
├── .gitignore
├── .hound.yml
├── .rspec
├── .rubocop.yml
├── .rubocop_todo.yml
├── .travis.yml
├── .vscode
    ├── launch.json
    └── settings.json
├── .yardopts
├── CHANGELOG.md
├── Dangerfile
├── Dockerfile
├── Gemfile
├── Guardfile
├── LICENSE
├── README.md
├── Rakefile
├── VISION.md
├── appveyor.yml
├── bin
    ├── console
    └── danger
├── build.sh
├── danger.gemspec
├── danger_plugins
    └── protect_files.rb
├── docs
    ├── guides
    │   ├── a_quick_ruby_overview.md
    │   ├── creating_your_first_plugin.md
    │   ├── dangerfile.md
    │   ├── faq.md
    │   ├── getting_started.html.slim
    │   ├── troubleshooting.md
    │   └── what_does_danger_do.md
    └── yard_support.rb
├── lib
    ├── assets
    │   └── DangerfileTemplate
    ├── danger.rb
    └── danger
    │   ├── ci_source
    │       ├── appcenter.rb
    │       ├── appcircle.rb
    │       ├── appveyor.rb
    │       ├── azure_pipelines.rb
    │       ├── bamboo.rb
    │       ├── bitbucket_pipelines.rb
    │       ├── bitrise.rb
    │       ├── buddybuild.rb
    │       ├── buildkite.rb
    │       ├── ci_source.rb
    │       ├── circle.rb
    │       ├── circle_api.rb
    │       ├── cirrus.rb
    │       ├── code_build.rb
    │       ├── codefresh.rb
    │       ├── codemagic.rb
    │       ├── codeship.rb
    │       ├── concourse.rb
    │       ├── custom_ci_with_github.rb
    │       ├── dotci.rb
    │       ├── drone.rb
    │       ├── github_actions.rb
    │       ├── gitlab_ci.rb
    │       ├── jenkins.rb
    │       ├── local_git_repo.rb
    │       ├── local_only_git_repo.rb
    │       ├── screwdriver.rb
    │       ├── semaphore.rb
    │       ├── support
    │       │   ├── commits.rb
    │       │   ├── find_repo_info_from_logs.rb
    │       │   ├── find_repo_info_from_url.rb
    │       │   ├── local_pull_request.rb
    │       │   ├── no_pull_request.rb
    │       │   ├── no_repo_info.rb
    │       │   ├── pull_request_finder.rb
    │       │   ├── remote_pull_request.rb
    │       │   └── repo_info.rb
    │       ├── surf.rb
    │       ├── teamcity.rb
    │       ├── travis.rb
    │       ├── xcode_cloud.rb
    │       └── xcode_server.rb
    │   ├── clients
    │       └── rubygems_client.rb
    │   ├── commands
    │       ├── dangerfile
    │       │   ├── gem.rb
    │       │   └── init.rb
    │       ├── dry_run.rb
    │       ├── init.rb
    │       ├── init_helpers
    │       │   └── interviewer.rb
    │       ├── local.rb
    │       ├── local_helpers
    │       │   ├── http_cache.rb
    │       │   ├── local_setup.rb
    │       │   └── pry_setup.rb
    │       ├── mr.rb
    │       ├── plugins
    │       │   ├── plugin_json.rb
    │       │   ├── plugin_lint.rb
    │       │   └── plugin_readme.rb
    │       ├── pr.rb
    │       ├── runner.rb
    │       ├── staging.rb
    │       └── systems.rb
    │   ├── comment_generators
    │       ├── bitbucket_server.md.erb
    │       ├── bitbucket_server_inline.md.erb
    │       ├── bitbucket_server_message_group.md.erb
    │       ├── github.md.erb
    │       ├── github_inline.md.erb
    │       ├── gitlab.md.erb
    │       ├── gitlab_inline.md.erb
    │       ├── vsts.md.erb
    │       └── vsts_inline.md.erb
    │   ├── core_ext
    │       ├── file_list.rb
    │       └── string.rb
    │   ├── danger_core
    │       ├── dangerfile.rb
    │       ├── dangerfile_dsl.rb
    │       ├── dangerfile_generator.rb
    │       ├── environment_manager.rb
    │       ├── executor.rb
    │       ├── message_aggregator.rb
    │       ├── message_group.rb
    │       ├── messages
    │       │   ├── base.rb
    │       │   ├── markdown.rb
    │       │   └── violation.rb
    │       ├── plugins
    │       │   ├── dangerfile_bitbucket_cloud_plugin.rb
    │       │   ├── dangerfile_bitbucket_server_plugin.rb
    │       │   ├── dangerfile_danger_plugin.rb
    │       │   ├── dangerfile_git_plugin.rb
    │       │   ├── dangerfile_github_plugin.rb
    │       │   ├── dangerfile_gitlab_plugin.rb
    │       │   ├── dangerfile_local_only_plugin.rb
    │       │   ├── dangerfile_messaging_plugin.rb
    │       │   └── dangerfile_vsts_plugin.rb
    │       └── standard_error.rb
    │   ├── helpers
    │       ├── array_subclass.rb
    │       ├── comment.rb
    │       ├── comments_helper.rb
    │       ├── comments_parsing_helper.rb
    │       ├── emoji_mapper.rb
    │       ├── find_max_num_violations.rb
    │       └── message_groups_array_helper.rb
    │   ├── plugin_support
    │       ├── gems_resolver.rb
    │       ├── plugin.rb
    │       ├── plugin_file_resolver.rb
    │       ├── plugin_linter.rb
    │       ├── plugin_parser.rb
    │       └── templates
    │       │   └── readme_table.html.erb
    │   ├── request_sources
    │       ├── bitbucket_cloud.rb
    │       ├── bitbucket_cloud_api.rb
    │       ├── bitbucket_server.rb
    │       ├── bitbucket_server_api.rb
    │       ├── code_insights_api.rb
    │       ├── github
    │       │   ├── github.rb
    │       │   ├── github_review.rb
    │       │   ├── github_review_resolver.rb
    │       │   └── github_review_unsupported.rb
    │       ├── gitlab.rb
    │       ├── local_only.rb
    │       ├── request_source.rb
    │       ├── support
    │       │   └── get_ignored_violation.rb
    │       ├── vsts.rb
    │       └── vsts_api.rb
    │   ├── scm_source
    │       └── git_repo.rb
    │   └── version.rb
└── spec
    ├── clients
        └── rubygems_client_spec.rb
    ├── danger
        └── helpers
        │   └── comment_spec.rb
    ├── danger_spec.rb
    ├── fixtures
        ├── bitbucket_cloud_api
        │   ├── oauth2_response.json
        │   ├── pr_comments.json
        │   ├── pr_response.json
        │   └── prs_response.json
        ├── bitbucket_server_api
        │   ├── pr_diff_response.json
        │   └── pr_response.json
        ├── ci_source
        │   ├── pull_request_event.json
        │   └── support
        │   │   ├── danger-git.log
        │   │   ├── danger_danger_pr_518.json
        │   │   ├── enterprise-remote.log
        │   │   ├── fork-pr.json
        │   │   ├── https-remote.log
        │   │   ├── remote.log
        │   │   ├── swiftweekly.github.io-git.log
        │   │   └── two-kinds-of-merge-both-present.log
        ├── circle_build_no_pr_field_response.json
        ├── circle_build_no_pr_response.json
        ├── circle_build_response.json
        ├── commands
        │   └── plugin_md_example.txt
        ├── comment_with_error.html
        ├── comment_with_error_and_warnings.html
        ├── comment_with_file_link.html
        ├── comment_with_non_sticky.html
        ├── comment_with_resolved_violation.html
        ├── dangerfile_with_error
        ├── dangerfile_with_error_and_path_reassignment
        ├── gemspecs
        │   └── danger-rubocop.gemspec
        ├── github
        │   ├── Dangerfile
        │   ├── swiftweekly.github.io-issues-89-comments.json
        │   ├── swiftweekly.github.io-issues-89.json
        │   └── swiftweekly.github.io-pulls-89.json
        ├── github_api
        │   ├── contents_response.json
        │   ├── danger_fork_repo.json
        │   ├── inline_comments.json
        │   ├── inline_comments_pr_diff_files.json
        │   ├── issue_comments.json
        │   ├── issue_response.json
        │   ├── pr_response.json
        │   ├── pr_review_response.json
        │   ├── pr_reviews_response.json
        │   └── repo_response.json
        ├── gitlab_api
        │   ├── commit_merge_requests.json
        │   ├── merge_request_1_changes_response.json
        │   ├── merge_request_1_comments_existing_danger_comment_response.json
        │   ├── merge_request_1_comments_no_stickies_response.json
        │   ├── merge_request_1_comments_response.json
        │   ├── merge_request_1_discussions_empty_response.json
        │   ├── merge_request_1_discussions_response.json
        │   ├── merge_request_1_response.json
        │   └── merge_requests_response.json
        ├── plugin_json
        │   ├── example_fully_doc.json
        │   └── example_remote.json
        ├── plugins
        │   ├── example_broken.rb
        │   ├── example_echo_plugin.rb
        │   ├── example_exact_path.rb
        │   ├── example_fully_documented.rb
        │   ├── example_globbing.rb
        │   ├── example_not_broken.rb
        │   ├── example_remote.rb
        │   └── plugin_many_methods.rb
        ├── pr_diff_response.diff
        ├── rubygems_api
        │   └── api_v1_versions_danger_latest.json
        └── vsts_api
        │   ├── danger_comments_response.json
        │   ├── no_danger_comments_response.json
        │   └── pr_response.json
    ├── lib
        └── danger
        │   ├── ci_sources
        │       ├── appcircle_spec.rb
        │       ├── appveyor_spec.rb
        │       ├── azure_pipelines_spec.rb
        │       ├── bamboo_spec.rb
        │       ├── bitbucket_pipelines_spec.rb
        │       ├── bitrise_spec.rb
        │       ├── buddybuild_spec.rb
        │       ├── buildkite_spec.rb
        │       ├── ci_source_spec.rb
        │       ├── circle_api_spec.rb
        │       ├── circle_spec.rb
        │       ├── code_build_spec.rb
        │       ├── codefresh_spec.rb
        │       ├── codemagic_spec.rb
        │       ├── concourse_spec.rb
        │       ├── custom_ci_with_github_spec.rb
        │       ├── dotci_spec.rb
        │       ├── drone_spec.rb
        │       ├── github_actions_spec.rb
        │       ├── gitlab_ci_spec.rb
        │       ├── jenkins_spec.rb
        │       ├── local_git_repo_spec.rb
        │       ├── local_only_git_repo_spec.rb
        │       ├── screwdriver_spec.rb
        │       ├── semaphore_spec.rb
        │       ├── support
        │       │   ├── find_repo_info_from_logs_spec.rb
        │       │   ├── find_repo_info_from_url_spec.rb
        │       │   └── pull_request_finder_spec.rb
        │       ├── surf_spec.rb
        │       ├── teamcity_spec.rb
        │       ├── travis_spec.rb
        │       ├── xcode_cloud_spec.rb
        │       └── xcode_server_spec.rb
        │   ├── commands
        │       ├── dry_run_spec.rb
        │       ├── init_helpers
        │       │   └── interviewer_spec.rb
        │       ├── init_spec.rb
        │       ├── local_helpers
        │       │   ├── http_cache_spec.rb
        │       │   ├── local_setup_spec.rb
        │       │   └── pry_setup_spec.rb
        │       ├── local_spec.rb
        │       ├── mr_spec.rb
        │       ├── plugin_json_spec.rb
        │       ├── plugins
        │       │   ├── plugin_lint_spec.rb
        │       │   └── plugin_readme_spec.rb
        │       ├── pr_spec.rb
        │       ├── runner_spec.rb
        │       └── staging_spec.rb
        │   ├── core_ext
        │       ├── file_list_spec.rb
        │       └── string_spec.rb
        │   ├── danger_core
        │       ├── danger_spec.rb
        │       ├── dangerfile_spec.rb
        │       ├── environment_manager_spec.rb
        │       ├── executor_spec.rb
        │       ├── message_aggregator_spec.rb
        │       ├── message_group_spec.rb
        │       ├── messages
        │       │   ├── markdown_spec.rb
        │       │   ├── shared_examples.rb
        │       │   └── violation_spec.rb
        │       ├── plugins
        │       │   ├── dangerfile_danger_plugin_spec.rb
        │       │   ├── dangerfile_git_plugin_spec.rb
        │       │   ├── dangerfile_github_plugin_spec.rb
        │       │   ├── dangerfile_gitlab_plugin_spec.rb
        │       │   ├── dangerfile_messaging_plugin_spec.rb
        │       │   └── dangerfile_vsts_plugin_spec.rb
        │       └── standard_error_spec.rb
        │   ├── helpers
        │       ├── array_subclass_spec.rb
        │       ├── comments_helper_spec.rb
        │       ├── emoji_mapper_spec.rb
        │       └── message_groups_array_helper_spec.rb
        │   ├── plugin_support
        │       ├── gems_resolver_spec.rb
        │       ├── plugin_file_resolver_spec.rb
        │       ├── plugin_linter_spec.rb
        │       ├── plugin_parser_spec.rb
        │       └── plugin_spec.rb
        │   ├── plugins
        │       ├── dangerfile_bitbucket_cloud_plugin_spec.rb
        │       └── dangerfile_bitbucket_server_plugin_spec.rb
        │   ├── request_sources
        │       ├── bitbucket_cloud_api_spec.rb
        │       ├── bitbucket_cloud_spec.rb
        │       ├── bitbucket_server_api_spec.rb
        │       ├── bitbucket_server_spec.rb
        │       ├── code_insights_api_spec.rb
        │       ├── github
        │       │   ├── github_review_resolver_spec.rb
        │       │   └── github_review_spec.rb
        │       ├── github_spec.rb
        │       ├── gitlab_spec.rb
        │       ├── local_only.rb
        │       ├── request_source_spec.rb
        │       ├── support
        │       │   └── get_ignored_violation_spec.rb
        │       ├── vsts_api_spec.rb
        │       └── vsts_spec.rb
        │   └── scm_source
        │       └── git_repo_spec.rb
    ├── spec_helper.rb
    └── support
        ├── bitbucket_cloud_helper.rb
        ├── bitbucket_server_helper.rb
        ├── ci_helper.rb
        ├── github_helper.rb
        ├── gitlab_helper.rb
        ├── matchers
            └── have_instance_variables_matcher.rb
        └── vsts_helper.rb


/.circleci/config.yml:
--------------------------------------------------------------------------------
 1 | version: 2
 2 | jobs:
 3 |   build:
 4 |     working_directory: ~/danger
 5 |     docker:
 6 |       - image: ruby:3.2
 7 |         environment:
 8 |           - RAILS_ENV=test
 9 |           - RACK_ENV=test
10 |     steps:
11 |       - checkout
12 |       - run: gem update --system 2.5.0
13 |       - run: gem install bundler -v '2.3.20'
14 |       - restore_cache:
15 |           key: gem-cache-{{ .Branch }}-{{ checksum "Gemfile" }}
16 |       - run: bundle install --path vendor/bundle
17 |       - save_cache:
18 |           key: gem-cache-{{ .Branch }}-{{ checksum "Gemfile" }}
19 |           paths:
20 |             - vendor/bundle
21 |       - run: git config --global user.email "danger@example.com"
22 |       - run: git config --global user.name "Danger McShane"
23 |       - run: bundle exec rake specs
24 |       - run: '[ ! -z $DANGER_GITHUB_API_TOKEN ] && bundle exec danger || echo "Skipping Danger for External Contributor"'
25 |       - run: bundle exec danger init --mousey --impatient
26 | 


--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
 1 | root = true
 2 | 
 3 | [*]
 4 | end_of_line = lf
 5 | insert_final_newline = true
 6 | charset = utf-8
 7 | trim_trailing_whitespace = true
 8 | 
 9 | [*.{rb,yml,gemspec}]
10 | indent_style = space
11 | indent_size = 2
12 | 
13 | [Dangerfile, Rakefile, Guardfile, Gemfile]
14 | indent_style = space
15 | indent_size = 2
16 | 


--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | CHANGELOG.md merge=union


--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 |   - package-ecosystem: github-actions
4 |     directory: "/"
5 |     schedule:
6 |       interval: "weekly"
7 | 


--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
 1 | # Report
 2 | 
 3 | ## What did you do?
 4 | 
 5 | ℹ Please replace this with what you did.
 6 | e.g. Run `danger local`
 7 | 
 8 | ## What did you expect to happen?
 9 | 
10 | ℹ Please replace this with what you expected to happen.
11 | e.g. Running your Dangerfile without it crashing.
12 | 
13 | ## What happened instead?
14 | 
15 | ℹ Please replace this with of what happened instead.
16 | e.g. 💥
17 | 
18 | ## Your Environment
19 | 
20 | * Which CI are you running on?
21 | * Are you running the latest version of Danger?
22 | * What is your Dangerfile?
23 | 
24 |   ```ruby
25 |   # please paste here, with 2-space indentation, thank you!
26 | 
27 |   ```
28 | 


--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
 1 | ///////////////////⚡///////////////////
 2 | 
 3 | # 🚫 AWESOME A PR!
 4 | 
 5 | - You can just delete all of this and start your PR text anytime -
 6 | 
 7 | Hello there, just a quick pre-warning of some of the Danger rules we run on Danger PRs.
 8 | 
 9 | The big one is we request that every code change to Danger include a CHANGELOG entry, 
10 | this is so that:
11 | 
12 | 1. People know what changes are between versions
13 | 2. Orta doesn't get all the credit for other people's work
14 | 
15 | Danger will look for a modification to the `CHANGELOG.md` when there are changes including
16 | `lib/*` - if you're fixing unreleased code, or doing a simple typo change, you can
17 | include `#trivial` in the title or the body of the PR and this is skipped.
18 | 
19 | We also request that you fill in the body of your PR, a title should be tweet length, but
20 | ideally you can explain the PRs reasoning in the body. We look that it's longer than 5 chars.
21 | 
22 | Other than that, a lot of the other Danger rules are trickier to trigger so we can address
23 | them as they come up!
24 | 
25 | ❤ THANKS FOR HELPING OUT :D 
26 | 
27 | ///////////////////⚡///////////////////


--------------------------------------------------------------------------------
/.github/workflows/CI.yml:
--------------------------------------------------------------------------------
 1 | name: CI
 2 | 
 3 | on:
 4 |   pull_request:
 5 |     branches:
 6 |       - master
 7 | 
 8 | concurrency:
 9 |   group: ${{ github.workflow }}-${{ github.ref }}
10 |   cancel-in-progress: true
11 | 
12 | jobs:
13 |   build:
14 |     strategy:
15 |       matrix:
16 |         os:
17 |           - macos-latest
18 |           - ubuntu-latest
19 |           - windows-latest
20 |         ruby-version:
21 |           - "2.7"
22 |           - "3.0"
23 |           - "3.1"
24 |           - "3.2"
25 |           - "3.3"
26 |           - "3.4"
27 |     runs-on: ${{ matrix.os }}
28 |     steps:
29 |       - uses: actions/checkout@v4
30 |         with:
31 |           fetch-depth: 0
32 |       - name: Set up Ruby
33 |         uses: ruby/setup-ruby@v1
34 |         with:
35 |           ruby-version: ${{ matrix.ruby-version }}
36 |           bundler-cache: true
37 | 
38 |       - name: Configure Git email/username
39 |         run: |
40 |           git config --global user.email "orta+dangersystems@artsy.net"
41 |           git config --global user.name "Danger.Systems"
42 | 
43 |       - name: Linting
44 |         run: bundle exec rubocop
45 | 
46 |       - name: Running Tests
47 |         run: bundle exec rake spec
48 | 
49 |       - name: Running the Dangerfile for this repo
50 |         if: runner.os != 'Windows'
51 |         run: |
52 |           TOKEN='7469b4e94ce21b43e3ab7a'
53 |           TOKEN+='79960c12a1e067f2ec'
54 |           DANGER_GITHUB_API_TOKEN=$TOKEN RUNNING_IN_ACTIONS=true echo 'bundle exec danger --verbose'
55 | 


--------------------------------------------------------------------------------
/.github/workflows/publish_package.yml:
--------------------------------------------------------------------------------
 1 | name: Release Danger package
 2 | 
 3 | on:
 4 |   push:
 5 |     branches:
 6 |       - 'master'
 7 |     tags:
 8 |       - '*'
 9 | 
10 | env:
11 |   REGISTRY: ghcr.io
12 |   IMAGE_NAME: ${{ github.repository }}
13 | 
14 | jobs:
15 |   build:
16 |     runs-on: ubuntu-latest
17 |     permissions:
18 |       contents: read
19 |       packages: write
20 | 
21 |     steps:
22 |       - name: Checkout
23 |         uses: actions/checkout@v4
24 | 
25 |       - name: Log in to the Container registry
26 |         uses: docker/login-action@v3
27 |         with:
28 |           registry: ${{ env.REGISTRY }}
29 |           username: ${{ github.actor }}
30 |           password: ${{ secrets.GITHUB_TOKEN }}
31 | 
32 |       - name: Set up Docker metadata
33 |         id: meta
34 |         uses: docker/metadata-action@v5
35 |         with:
36 |           images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
37 |           tags: |
38 |             type=ref,event=branch
39 |             type=ref,event=tag
40 | 
41 |       - name: Build and push Docker image
42 |         uses: docker/build-push-action@v6
43 |         with:
44 |           context: .
45 |           push: true
46 |           tags: ${{ steps.meta.outputs.tags }}
47 |           labels: ${{ steps.meta.outputs.labels }}
48 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | /.bundle/
 2 | /.yardoc
 3 | /Gemfile.lock
 4 | /_yardoc/
 5 | /coverage/
 6 | /doc/
 7 | /pkg/
 8 | /spec/reports/
 9 | /tmp/
10 | *.gem
11 | *.rbc
12 | /.config
13 | /coverage/
14 | /InstalledFiles
15 | /pkg/
16 | /spec/reports/
17 | /test/tmp/
18 | /test/version_tmp/
19 | /tmp/
20 | 
21 | ## Specific to RubyMotion:
22 | .dat*
23 | .repl_history
24 | build/
25 | 
26 | ## Documentation cache and generated files:
27 | /.yardoc/
28 | /_yardoc/
29 | /doc/
30 | /rdoc/
31 | 
32 | ## Environment normalisation:
33 | /.bundle/
34 | /vendor/bundle
35 | /lib/bundler/man/
36 | /vendor/
37 | /bundle/
38 | 
39 | # for a library or gem, you might want to ignore these files since the code is
40 | # intended to run in multiple environments; otherwise, check them in:
41 | # Gemfile.lock
42 | # .ruby-version
43 | # .ruby-gemset
44 | 
45 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
46 | .rvmrc
47 | .keys
48 | .idea
49 | junit-results.xml
50 | 
51 | # RSpec
52 | /spec/examples.txt
53 | 
54 | # test-queue
55 | .test_queue_stats
56 | 
57 | # Byebug
58 | /.byebug_history
59 | 
60 | # our forked RSpec JUnit Formatter
61 | /.xml_dump_failures
62 | 
63 | # Mac OS
64 | .DS_Store
65 | 


--------------------------------------------------------------------------------
/.hound.yml:
--------------------------------------------------------------------------------
1 | ruby:
2 |   config_file: .rubocop.yml
3 | 


--------------------------------------------------------------------------------
/.rspec:
--------------------------------------------------------------------------------
1 | --format documentation
2 | --color
3 | --require spec_helper
4 | --format RspecJunitFormatter --out junit-results.xml
5 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
 1 | language: ruby
 2 | cache:
 3 |   directories:
 4 |     - bundle
 5 | 
 6 | rvm:
 7 |   - 3.2.2
 8 | 
 9 | bundler_args: "--without documentation --path bundle"
10 | 
11 | before_install:
12 |   - git fetch --depth=1000000
13 |   - gem install bundler -v '2.3.20'
14 | 
15 | after_install:
16 |   - rake install
17 | 
18 | before_script:
19 |   # Tests use real git commands
20 |   - git config --global user.email "danger@example.com"
21 |   - git config --global user.name "Danger McShane"
22 | 
23 | script:
24 |   - echo "Running Tests"
25 |   - bundle exec rake spec
26 |   - echo "Linting the documentation for Danger's internal plugins"
27 |   - bundle exec danger plugins lint lib/danger/danger_core/plugins/*.rb --warnings-as-errors
28 |   - echo "Running the Dangerfile for this repo"
29 |   - bundle exec danger --verbose
30 |   - echo "Validating that danger init is working fine"
31 |   - bundle exec danger init --mousey --impatient
32 | 
33 | sudo: required
34 | 


--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "version": "0.2.0",
 3 |   "configurations": [
 4 |     {
 5 |       "name": "Debug Local File",
 6 |       "type": "Ruby",
 7 |       "request": "launch",
 8 |       "cwd": "${workspaceRoot}",
 9 |       "program": "${workspaceRoot}/main.rb"
10 |     },
11 |     {
12 |       "name": "Listen for rdebug-ide",
13 |       "type": "Ruby",
14 |       "request": "attach",
15 |       "cwd": "${workspaceRoot}",
16 |       "remoteHost": "127.0.0.1",
17 |       "remotePort": "1234",
18 |       "remoteWorkspaceRoot": "${workspaceRoot}"
19 |     },
20 |     {
21 |       "name": "Rails server",
22 |       "type": "Ruby",
23 |       "request": "launch",
24 |       "cwd": "${workspaceRoot}",
25 |       "program": "${workspaceRoot}/bin/rails",
26 |       "args": [
27 |         "server"
28 |       ]
29 |     },
30 |     {
31 |       "name": "RSpec - all",
32 |       "type": "Ruby",
33 |       "request": "launch",
34 |       "cwd": "${workspaceRoot}",
35 |       "program": "${workspaceRoot}/bin/rspec",
36 |       "args": [
37 |         "-I",
38 |         "${workspaceRoot}"
39 |       ]
40 |     },
41 |     {
42 |       "name": "RSpec - active spec file only",
43 |       "type": "Ruby",
44 |       "request": "launch",
45 |       "cwd": "${workspaceRoot}",
46 |       "program": "${workspaceRoot}/bin/rspec",
47 |       "args": [
48 |         "-I",
49 |         "${workspaceRoot}",
50 |         "${file}"
51 |       ]
52 |     },
53 |     {
54 |       "name": "Cucumber",
55 |       "type": "Ruby",
56 |       "request": "launch",
57 |       "cwd": "${workspaceRoot}",
58 |       "program": "${workspaceRoot}/bin/cucumber"
59 |     }
60 |   ]
61 | }


--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 |     "search.exclude": {
3 |         "coverage/": true,
4 |         ".xml_dump_failures": true
5 |     }
6 | }
7 | 


--------------------------------------------------------------------------------
/.yardopts:
--------------------------------------------------------------------------------
1 | --markup=markdown
2 | --load docs/yard_support.rb
3 | --tag tags:tags
4 | --tag availability:availability
5 | 


--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
 1 | FROM ruby:3.2
 2 | 
 3 | LABEL "com.github.actions.name"="Danger"
 4 | LABEL "com.github.actions.description"="Runs danger in a docker container such as GitHub Actions"
 5 | LABEL "com.github.actions.icon"="mic"
 6 | LABEL "com.github.actions.color"="purple"
 7 | LABEL "repository"="https://github.com/danger/danger"
 8 | LABEL "homepage"="https://github.com/danger/danger"
 9 | LABEL "maintainer"="Rishabh Tayal <rtayal11@gmail.com>"
10 | LABEL "maintainer"="Orta Therox"
11 | 
12 | RUN apt-get update -qq && apt-get install -y build-essential p7zip unzip
13 | 
14 | # See https://github.com/actions/runner/issues/2033
15 | RUN git config --system --add safe.directory /github/workspace
16 | 
17 | RUN mkdir /myapp
18 | WORKDIR /myapp
19 | COPY . /myapp
20 | 
21 | RUN gem install bundler
22 | 
23 | ENV BUNDLE_GEMFILE=/myapp/Gemfile
24 | RUN bundle install
25 | ENTRYPOINT ["bundle", "exec", "danger"]
26 | 


--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
 1 | source "https://rubygems.org"
 2 | 
 3 | gemspec
 4 | 
 5 | gem "bundler"
 6 | gem "chandler"
 7 | gem "danger-gitlab"
 8 | gem "danger-junit", "~> 0.5"
 9 | gem "faraday-http-cache", git: "https://github.com/sourcelevel/faraday-http-cache.git"
10 | gem "fuubar", "~> 2.5"
11 | gem "guard", "~> 2.16"
12 | gem "guard-rspec", "~> 4.7"
13 | gem "guard-rubocop", "~> 1.2"
14 | gem "listen", "3.0.7"
15 | gem "pry", "~> 0.13"
16 | gem "pry-byebug"
17 | gem "rake", "~> 13.0"
18 | gem "rspec", "~> 3.9"
19 | gem "rspec_junit_formatter", "~> 0.4"
20 | gem "rubocop", "~> 1.74.0"
21 | gem "simplecov", "~> 0.18"
22 | gem "test-queue"
23 | gem "webmock", "~> 3.16.2"
24 | gem "yard", "~> 0.9.11"
25 | 


--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
 1 | # A sample Guardfile
 2 | # More info at https://github.com/guard/guard#readme
 3 | 
 4 | # To run, use `guard`.
 5 | 
 6 | group :red_green_refactor, halt_on_fail: true do
 7 |   guard :rspec, cmd: "bundle exec rspec" do
 8 |     require "guard/rspec/dsl"
 9 |     dsl = Guard::RSpec::Dsl.new(self)
10 | 
11 |     # Feel free to open issues for suggestions and improvements
12 | 
13 |     # RSpec files
14 |     rspec = dsl.rspec
15 |     watch(rspec.spec_helper) { rspec.spec_dir }
16 |     watch(rspec.spec_support) { rspec.spec_dir }
17 |     watch(rspec.spec_files)
18 | 
19 |     # Ruby files
20 |     ruby = dsl.ruby
21 |     dsl.watch_spec_files_for(ruby.lib_files)
22 |   end
23 | 
24 |   guard :rubocop
25 | end
26 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2015-present Orta, Felix Krause
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 
23 | 


--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
 1 | begin
 2 |   require "bundler/gem_tasks"
 3 |   require "rspec/core/rake_task"
 4 |   RSpec::Core::RakeTask.new(:specs)
 5 |   require "rubocop/rake_task"
 6 |   RuboCop::RakeTask.new
 7 | rescue LoadError
 8 |   puts "Please use `bundle exec` to get all the rake commands"
 9 | end
10 | 
11 | task default: %w(rubocop spec)
12 | 
13 | desc "Danger's tests"
14 | task :spec do
15 |   if Process.respond_to?(:fork)
16 |     sh("rspec-queue")
17 |   else
18 |     sh("rspec")
19 |   end
20 | 
21 |   Rake::Task["spec_docs"].invoke
22 | end
23 | 
24 | desc "Tests that the core documentation is up to snuff"
25 | task :spec_docs do
26 |   core_plugins = Dir.glob("lib/danger/danger_core/plugins/*.rb")
27 |   sh "danger plugins lint #{core_plugins.join ' '}"
28 |   sh "danger systems ci_docs"
29 | end
30 | 
31 | desc "I do this so often now, better to just handle it here"
32 | task :guard do |task|
33 |   sh "bundle exec guard"
34 | end
35 | 
36 | desc "Runs chandler for current version"
37 | task :chandler do
38 |   lib = File.expand_path("lib", __dir__)
39 |   $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
40 |   require "danger/version"
41 |   if ENV["CHANDLER_GITHUB_API_TOKEN"]
42 |     sh "bundle exec chandler push #{Danger::VERSION}"
43 |   elsif ENV["DANGER_GITHUB_API_TOKEN"]
44 |     sh "CHANDLER_GITHUB_API_TOKEN=#{ENV['DANGER_GITHUB_API_TOKEN']} bundle exec chandler push #{Danger::VERSION}"
45 |   else
46 |     puts "Skipping chandler due to no `CHANDLER_GITHUB_API_TOKEN` or `DANGER_GITHUB_API_TOKEN` in the ENV."
47 |   end
48 | end
49 | 
50 | Rake::Task["release"].enhance do
51 |   Rake::Task["chandler"].invoke
52 | end
53 | 


--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
 1 | install:
 2 |   - set PATH=C:\Ruby27\bin;%PATH%
 3 |   - gem uninstall bundler --executables
 4 |   - gem install bundler -v '2.3.20'
 5 |   - bundle install
 6 | 
 7 | build: off
 8 | 
 9 | test_script:
10 |   # Tests use real git commands
11 |   - git config --global user.email "danger@example.com"
12 |   - git config --global user.name "Danger McShane"
13 |   - bundle exec rake specs
14 | 


--------------------------------------------------------------------------------
/bin/console:
--------------------------------------------------------------------------------
 1 | #!/usr/bin/env ruby
 2 | 
 3 | require "bundler/setup"
 4 | require "danger"
 5 | 
 6 | # You can add fixtures and/or initialization code here to make experimenting
 7 | # with your gem easier. You can also use a different console, if you like.
 8 | 
 9 | # (If you use this, don't forget to add pry to your Gemfile!)
10 | # require "pry"
11 | # Pry.start
12 | 
13 | require "irb"
14 | IRB.start
15 | 


--------------------------------------------------------------------------------
/bin/danger:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | $LOAD_PATH.push File.expand_path("../lib", __dir__)
3 | 
4 | require "danger"
5 | Danger::Runner.run ARGV
6 | 


--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
 1 | gem install bundler
 2 | bundle exec rake spec
 3 | 
 4 | if [-z "$SURF_BUILD_NAME" ]; then
 5 | 	## Posting to GitHub
 6 | 	bundle exec danger
 7 | else
 8 | 	## Local clean build, just print to console
 9 | 	bundle exec danger local
10 | fi
11 | 


--------------------------------------------------------------------------------
/danger.gemspec:
--------------------------------------------------------------------------------
 1 | lib = File.expand_path("lib", __dir__)
 2 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
 3 | require "danger/version"
 4 | Gem::Specification.new do |spec|
 5 |   spec.name          = "danger"
 6 |   spec.version       = Danger::VERSION
 7 |   spec.authors       = ["Orta Therox", "Juanito Fatas"]
 8 |   spec.email         = ["orta.therox@gmail.com", "katehuang0320@gmail.com"]
 9 |   spec.license       = "MIT"
10 | 
11 |   spec.summary       = Danger::DESCRIPTION
12 |   spec.description   = "Stop Saying 'You Forgot To…' in Code Review"
13 |   spec.homepage      = "https://github.com/danger/danger"
14 | 
15 |   spec.files         = Dir["lib/**/*"] + %w(bin/danger README.md LICENSE)
16 |   spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17 |   spec.require_paths = ["lib"]
18 | 
19 |   spec.required_ruby_version = ">= 2.7.0"
20 | 
21 |   spec.add_runtime_dependency "base64", "~> 0.2"
22 |   spec.add_runtime_dependency "claide", "~> 1.0"
23 |   spec.add_runtime_dependency "claide-plugins", ">= 0.9.2"
24 |   spec.add_runtime_dependency "colored2", ">= 3.1", "< 5"
25 |   spec.add_runtime_dependency "cork", "~> 0.1"
26 |   spec.add_runtime_dependency "faraday", ">= 0.9.0", "< 3.0"
27 |   spec.add_runtime_dependency "faraday-http-cache", "~> 2.0"
28 |   spec.add_runtime_dependency "git", ">= 1.13", "< 3.0"
29 |   spec.add_runtime_dependency "kramdown", ">= 2.5.1", "< 3.0"
30 |   spec.add_runtime_dependency "kramdown-parser-gfm", "~> 1.0"
31 |   spec.add_runtime_dependency "octokit", ">= 4.0"
32 |   spec.add_runtime_dependency "pstore", "~> 0.1"
33 |   spec.add_runtime_dependency "terminal-table", ">= 1", "< 5"
34 | end
35 | 


--------------------------------------------------------------------------------
/danger_plugins/protect_files.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Files < Plugin
 3 |     def protect_files(path: nil, message: nil, fail_build: true)
 4 |       raise "You have to provide a message" if message.to_s.empty?
 5 |       raise "You have to provide a path" if path.to_s.empty?
 6 | 
 7 |       broken_rule = git.modified_files.include?(path)
 8 | 
 9 |       return unless broken_rule
10 |       fail_build ? fail(message) : warn(message)
11 |     end
12 |   end
13 | end
14 | 


--------------------------------------------------------------------------------
/docs/guides/dangerfile.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | title: The Dangerfile
 3 | subtitle: The Dangerfile
 4 | layout: guide
 5 | order: 2
 6 | ---
 7 | 
 8 | A `Dangerfile` is a [Ruby DSL][dsl]. Before the ruby code inside your `Dangerfile` is executed, she grabs useful bits of data about: the CI environment, the git diff, and the code review details. There is a full writeup of what happens in ["What does Danger do?"][wot_do]. For now that's enough.
 9 | 
10 | The `Dangerfile` is where you create your rules, Danger comes with no rules set up by default. This is on purpose, we don't know your culture.
11 | 
12 | We've found it easier to start with something as simple as 
13 | 
14 | ```ruby
15 | if github.pr_body.length < 5
16 |   fail "Please provide a summary in the Pull Request description"
17 | end
18 | ```
19 | 
20 | Which is a pretty safe bet. Then over time, when your team notices that something can be easily automated, you add it as another rule. Bit by bit.
21 | 
22 | Where to go from here:
23 | - [Working locally on your `Dangerfile`][troubleshooting]
24 | 
25 | [wot_do]: /guides/what_does_danger_do.html
26 | [dsl]: https://www.infoq.com/news/2007/06/dsl-or-not
27 | [troubleshooting]: /guides/troubleshooting.html#i-want-to-work-locally-on-my-dangerfile
28 | 


--------------------------------------------------------------------------------
/docs/guides/what_does_danger_do.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | title: What does Danger do?
 3 | subtitle: Danger wot
 4 | layout: guide
 5 | order: 3
 6 | ---
 7 | 
 8 | Danger makes it easy to create feedback loops in code reviews through automation. This makes it possible to move cultural norms within your team into code, as well as easily share them with the world.
 9 | 
10 | To pull that off, Danger needs to be able to run inside your continuous integration (CI) environment, and to be able to provide feedback to your code review platform. This document describes what happens when you run `bundle exec danger`.
11 | 
12 | 1. First she sets up the core plugins for Danger, these are the classes documented in the reference. They provide the user API for Danger.
13 | 1. Next she determines if she's running on a CI service she recognizes. She does this by [looking][bitrise_example] at the environment variables in your console.
14 | 1. After being sure about the environment, she checks if this is a code review build. For single commit / merge builds, Danger does not run.
15 | 1. With the environment set up, she generates diff information, and pulls down status information for the code review.
16 | 1. Danger then runs your local `Dangerfile`.
17 | 1. After parsing the local `Dangerfile`, she then checks for an [organisation][multi_repos] `Dangerfile` and runs that if it exists.
18 | 1. Danger then posts a comment into your code review page showing the results of the `Dangerfile`s.
19 | 1. Finally Danger either fails the build, or exits with a successful exit code.
20 | 
21 | ### Plugins
22 | 
23 | Danger was built with a plugin structure in mind from day one. The [core of Danger itself aims to be small][vision], with space for others to easily build sharable plugins that extend Danger to fix common issues. All of the Danger API is built in plugins.
24 | 
25 | To simplify the experience for consumers of plugins, Danger does very little. Each plugin adds an instance of the plugin's class into the `Dangerfile`, plugins are then free to use their own methods and store their own data in memory. One of the up-sides of this is that if you want to take some code from your `Dangerfile`, and turn it into a plugin - it would be source-compatible.
26 | 
27 | [multi_repos]: /guides/faq.html#i-want-to-run-danger-across-multiple-repos
28 | [vision]: https://github.com/danger/danger/blob/master/VISION.md
29 | [bitrise_example]: https://github.com/danger/danger/blob/e98dc7156268adcd132d114d02d7935375f42452/lib/danger/ci_source/bitrise.rb
30 | 


--------------------------------------------------------------------------------
/docs/yard_support.rb:
--------------------------------------------------------------------------------
 1 | # frozen_string_literal: true
 2 | 
 3 | require "kramdown"
 4 | require "kramdown-parser-gfm"
 5 | 
 6 | # Custom markup provider class that always renders Kramdown using GFM (Github Flavored Markdown).
 7 | # @see https://stackoverflow.com/a/63683511/6918498
 8 | class KramdownGfmDocument < Kramdown::Document
 9 |   def initialize(source, options = {})
10 |     options[:input] = "GFM" unless options.key?(:input)
11 |     super(source, options)
12 |   end
13 | end
14 | 
15 | # Register the new provider as the highest priority option for Markdown.
16 | YARD::Templates::Helpers::MarkupHelper::MARKUP_PROVIDERS[:markdown].insert(0, { const: KramdownGfmDocument.name })
17 | 


--------------------------------------------------------------------------------
/lib/assets/DangerfileTemplate:
--------------------------------------------------------------------------------
 1 | # Sometimes it's a README fix, or something like that - which isn't relevant for
 2 | # including in a project's CHANGELOG for example
 3 | declared_trivial = github.pr_title.include? "#trivial"
 4 | 
 5 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet
 6 | warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]"
 7 | 
 8 | # Warn when there is a big PR
 9 | warn("Big PR") if git.lines_of_code > 500
10 | 
11 | # Don't let testing shortcuts get into master by accident
12 | fail("fdescribe left in tests") if `grep -r fdescribe specs/ `.length > 1
13 | fail("fit left in tests") if `grep -r fit specs/ `.length > 1
14 | 


--------------------------------------------------------------------------------
/lib/danger.rb:
--------------------------------------------------------------------------------
 1 | require "danger/version"
 2 | require "danger/danger_core/dangerfile"
 3 | require "danger/danger_core/environment_manager"
 4 | require "danger/commands/runner"
 5 | require "danger/plugin_support/plugin"
 6 | require "danger/core_ext/string"
 7 | require "danger/danger_core/executor"
 8 | 
 9 | require "claide"
10 | require "colored2"
11 | require "pathname"
12 | require "terminal-table"
13 | require "cork"
14 | 
15 | # Import all the Sources (CI, Request and SCM)
16 | Dir[File.expand_path("danger/*source/*.rb", File.dirname(__FILE__))].each do |file|
17 |   require file
18 | end
19 | 
20 | module Danger
21 |   GEM_NAME = "danger".freeze
22 | 
23 |   # @return [String] The path to the local gem directory
24 |   def self.gem_path
25 |     if Gem::Specification.find_all_by_name(GEM_NAME).empty?
26 |       raise "Couldn't find gem directory for 'danger'"
27 |     end
28 | 
29 |     return Gem::Specification.find_by_name(GEM_NAME).gem_dir
30 |   end
31 | 
32 |   # @return [String] Latest version of Danger on https://rubygems.org
33 |   def self.danger_outdated?
34 |     require "danger/clients/rubygems_client"
35 |     latest_version = RubyGemsClient.latest_danger_version
36 | 
37 |     if Gem::Version.new(latest_version) > Gem::Version.new(Danger::VERSION)
38 |       latest_version
39 |     else
40 |       false
41 |     end
42 |   rescue StandardError => _e
43 |     false
44 |   end
45 | end
46 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/appcenter.rb:
--------------------------------------------------------------------------------
 1 | # https://docs.microsoft.com/en-us/appcenter/build/custom/variables/
 2 | require "uri"
 3 | require "danger/request_sources/github/github"
 4 | 
 5 | module Danger
 6 |   # ### CI Setup
 7 |   #
 8 |   # Add a script step to your appcenter-post-build.sh:
 9 |   #
10 |   # ```shell
11 |   #   #!/usr/bin/env bash
12 |   #   bundle install
13 |   #   bundle exec danger
14 |   # ```
15 |   #
16 |   # ### Token Setup
17 |   #
18 |   # Add the `DANGER_GITHUB_API_TOKEN` to your environment variables.
19 |   #
20 |   class Appcenter < CI
21 |     def self.validates_as_ci?(env)
22 |       env.key? "APPCENTER_BUILD_ID"
23 |     end
24 | 
25 |     def self.validates_as_pr?(env)
26 |       return env["BUILD_REASON"] == "PullRequest"
27 |     end
28 | 
29 |     def self.owner_for_github(env)
30 |       URI.parse(env["BUILD_REPOSITORY_URI"]).path.split("/")[1]
31 |     end
32 | 
33 |     def self.repo_identifier_for_github(env)
34 |       repo_name = env["BUILD_REPOSITORY_NAME"]
35 |       owner = owner_for_github(env)
36 |       "#{owner}/#{repo_name}"
37 |     end
38 | 
39 |     # Hopefully it's a temporary workaround (same as in Codeship integration) because App Center
40 |     # doesn't expose PR's ID. There's a future request https://github.com/Microsoft/appcenter/issues/79
41 |     def self.pr_from_env(env)
42 |       Danger::RequestSources::GitHub.new(nil, env).get_pr_from_branch(repo_identifier_for_github(env), env["BUILD_SOURCEBRANCHNAME"], owner_for_github(env))
43 |     end
44 | 
45 |     def supported_request_sources
46 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
47 |     end
48 | 
49 |     def initialize(env)
50 |       self.pull_request_id = self.class.pr_from_env(env)
51 |       self.repo_url = env["BUILD_REPOSITORY_URI"]
52 |       self.repo_slug = self.class.repo_identifier_for_github(env)
53 |     end
54 |   end
55 | end
56 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/appveyor.rb:
--------------------------------------------------------------------------------
 1 | # https://www.appveyor.com/docs/build-configuration/
 2 | module Danger
 3 |   # ### CI Setup
 4 |   #
 5 |   # Install dependencies and add a danger step to your `appveyor.yml`.
 6 |   #
 7 |   # ```yaml
 8 |   # install:
 9 |   # - cmd: >-
10 |   #     set PATH=C:\Ruby25-x64\bin;%PATH%
11 |   #
12 |   #     bundle install
13 |   # after_test:
14 |   # - cmd: >-
15 |   #     bundle exec danger
16 |   # ```
17 |   #
18 |   # ### Token Setup
19 |   #
20 |   # For public repositories, add your plain token to environment variables in `appveyor.yml`.
21 |   # Encrypted environment variables will not be decrypted on PR builds.
22 |   # see here: https://www.appveyor.com/docs/build-configuration/#secure-variables
23 |   #
24 |   # ```yaml
25 |   # environment:
26 |   #   DANGER_GITHUB_API_TOKEN: <YOUR_TOKEN_HERE>
27 |   # ```
28 |   #
29 |   # For private repositories, enter your token in `Settings>Environment>Environment variables>Add variable` and turn on `variable encryption`.
30 |   # You will see encrypted variable text in `Settings>Export YAML` so just copy to your `appveyor.yml`.
31 |   #
32 |   # ```yaml
33 |   # environment:
34 |   #   DANGER_GITHUB_API_TOKEN:
35 |   #     secure: <YOUR_ENCRYPTED_TOKEN_HERE>
36 |   # ```
37 |   #
38 |   class AppVeyor < CI
39 |     def self.validates_as_ci?(env)
40 |       env.key? "APPVEYOR"
41 |     end
42 | 
43 |     def self.validates_as_pr?(env)
44 |       return false unless env.key? "APPVEYOR_PULL_REQUEST_NUMBER"
45 | 
46 |       env["APPVEYOR_PULL_REQUEST_NUMBER"].to_i > 0
47 |     end
48 | 
49 |     def initialize(env)
50 |       self.repo_slug = env["APPVEYOR_REPO_NAME"]
51 |       self.pull_request_id = env["APPVEYOR_PULL_REQUEST_NUMBER"]
52 |       self.repo_url = GitRepo.new.origins # AppVeyor doesn't provide a repo url env variable for now
53 |     end
54 | 
55 |     def supported_request_sources
56 |       @supported_request_sources ||= [
57 |         Danger::RequestSources::GitHub,
58 |         Danger::RequestSources::BitbucketCloud,
59 |         Danger::RequestSources::BitbucketServer,
60 |         Danger::RequestSources::GitLab
61 |       ]
62 |     end
63 |   end
64 | end
65 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/bamboo.rb:
--------------------------------------------------------------------------------
 1 | require "set"
 2 | 
 3 | module Danger
 4 |   # ### CI Setup
 5 |   #
 6 |   # Add a Run Script task that executes `danger` (or `bundle exec danger` if you're using Bundler
 7 |   # to manage your gems) as your as part of your Bamboo plan.
 8 |   # The minimum supported version is Bamboo 6.9.
 9 |   #
10 |   # ### Token Setup
11 |   #
12 |   # IMPORTANT: All required Bamboo environment variables will be available
13 |   # only if the plan is run as part of a pull request. This can be achieved by selecting:
14 |   # Configure plan -> Branches -> Create plan branch: "When pull request is created".
15 |   # Otherwise, `bamboo_repository_pr_key` and `bamboo_planRepository_repositoryUrl`
16 |   # will not be available.
17 |   #
18 |   class Bamboo < CI
19 |     def supported_request_sources
20 |       @supported_request_sources ||= [
21 |         Danger::RequestSources::BitbucketServer
22 |       ]
23 |     end
24 | 
25 |     def self.validates_as_ci?(env)
26 |       env.key? "bamboo_buildKey"
27 |     end
28 | 
29 |     def self.validates_as_pr?(env)
30 |       exists = ["bamboo_repository_pr_key", "bamboo_planRepository_repositoryUrl"].all? { |x| env[x] && !env[x].empty? }
31 |       exists && env["bamboo_repository_pr_key"].to_i > 0
32 |     end
33 | 
34 |     def initialize(env)
35 |       self.repo_url = env["bamboo_planRepository_repositoryUrl"]
36 |       self.pull_request_id = env["bamboo_repository_pr_key"]
37 |       repo_matches = self.repo_url.match(%r{([/:])([^/]+/[^/]+?)(\.git$|$)})
38 |       self.repo_slug = repo_matches[2] unless repo_matches.nil?
39 |     end
40 |   end
41 | end
42 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/bitbucket_pipelines.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   # ### CI Setup
 3 |   #
 4 |   # Install dependencies and add a danger step to your `bitbucket-pipelines.yml`.
 5 |   #
 6 |   # ```yaml
 7 |   #   script:
 8 |   #     - bundle exec danger --verbose
 9 |   # ```
10 |   #
11 |   # ### Token Setup
12 |   #
13 |   # For username and password, you need to set.
14 |   #
15 |   # - `DANGER_BITBUCKETCLOUD_USERNAME` = The username for the account used to comment, as shown on
16 |   #   https://bitbucket.org/account/
17 |   # - `DANGER_BITBUCKETCLOUD_PASSWORD` = The password for the account used to comment, you could use
18 |   #   [App passwords](https://confluence.atlassian.com/bitbucket/app-passwords-828781300.html#Apppasswords-Aboutapppasswords)
19 |   #   with Read Pull Requests and Read Account Permissions.
20 |   #
21 |   # For OAuth key and OAuth secret, you can get them from.
22 |   #
23 |   # - Open [BitBucket Cloud Website](https://bitbucket.org)
24 |   # - Navigate to Settings > OAuth > Add consumer
25 |   # - Put `https://bitbucket.org/site/oauth2/authorize` for `Callback URL`, and enable Read Pull requests, and Read Account
26 |   #   Permission.
27 |   #
28 |   # - `DANGER_BITBUCKETCLOUD_OAUTH_KEY` = The consumer key for the account used to comment, as show as `Key` on the website.
29 |   # - `DANGER_BITBUCKETCLOUD_OAUTH_SECRET` = The consumer secret for the account used to comment, as show as `Secret` on the
30 |   #   website.
31 |   #
32 |   # For [repository access token](https://support.atlassian.com/bitbucket-cloud/docs/repository-access-tokens/), what you
33 |   # need to create one is:
34 |   #
35 |   # - Open your repository URL
36 |   # - Navigate to Settings > Security > Access Tokens > Create Repository Access Token
37 |   # - Give it a name and set Pull requests write scope
38 | 
39 |   class BitbucketPipelines < CI
40 |     def self.validates_as_ci?(env)
41 |       env.key? "BITBUCKET_BUILD_NUMBER"
42 |     end
43 | 
44 |     def self.validates_as_pr?(env)
45 |       env.key? "BITBUCKET_PR_ID"
46 |     end
47 | 
48 |     def supported_request_sources
49 |       @supported_request_sources ||= [Danger::RequestSources::BitbucketCloud]
50 |     end
51 | 
52 |     def initialize(env)
53 |       self.repo_url = env["BITBUCKET_GIT_HTTP_ORIGIN"]
54 |       self.repo_slug = "#{env['BITBUCKET_REPO_OWNER']}/#{env['BITBUCKET_REPO_SLUG']}"
55 |       self.pull_request_id = env["BITBUCKET_PR_ID"]
56 |     end
57 |   end
58 | end
59 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/buildkite.rb:
--------------------------------------------------------------------------------
 1 | # https://buildkite.com/docs/agent/osx
 2 | # https://buildkite.com/docs/guides/environment-variables
 3 | require "danger/request_sources/github/github"
 4 | require "danger/request_sources/gitlab"
 5 | 
 6 | module Danger
 7 |   # ### CI Setup
 8 |   #
 9 |   # With BuildKite you run the server yourself, so you will want to run  it as a part of your build process.
10 |   # It is common to have build steps, so we would recommend adding this to your script:
11 |   #
12 |   #  ```shell
13 |   #   echo "--- Running Danger"
14 |   #   bundle exec danger
15 |   #  ```
16 |   #
17 |   # ### Token Setup
18 |   #
19 |   # #### GitHub
20 |   #
21 |   # As this is self-hosted, you will need to add the `DANGER_GITHUB_API_TOKEN` to your build user's ENV. The alternative
22 |   # is to pass in the token as a prefix to the command `DANGER_GITHUB_API_TOKEN="123" bundle exec danger`.
23 |   #
24 |   # #### GitLab
25 |   #
26 |   # As this is self-hosted, you will need to add the `DANGER_GITLAB_API_TOKEN` to your build user's ENV. The alternative
27 |   # is to pass in the token as a prefix to the command `DANGER_GITLAB_API_TOKEN="123" bundle exec danger`.
28 |   #
29 |   class Buildkite < CI
30 |     def self.validates_as_ci?(env)
31 |       env.key? "BUILDKITE"
32 |     end
33 | 
34 |     def self.validates_as_pr?(env)
35 |       exists = ["BUILDKITE_PULL_REQUEST_REPO", "BUILDKITE_PULL_REQUEST"].all? { |x| env[x] }
36 |       exists && !env["BUILDKITE_PULL_REQUEST_REPO"].empty?
37 |     end
38 | 
39 |     def initialize(env)
40 |       self.repo_url = env["BUILDKITE_REPO"]
41 |       self.pull_request_id = env["BUILDKITE_PULL_REQUEST"]
42 | 
43 |       repo_matches = self.repo_url.match(%r{([/:])([^/]+/[^/]+?)(\.git$|$)})
44 |       self.repo_slug = repo_matches[2] unless repo_matches.nil?
45 |     end
46 | 
47 |     def supported_request_sources
48 |       @supported_request_sources ||= [Danger::RequestSources::GitHub, Danger::RequestSources::GitLab, Danger::RequestSources::BitbucketServer]
49 |     end
50 |   end
51 | end
52 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/ci_source.rb:
--------------------------------------------------------------------------------
 1 | require "set"
 2 | 
 3 | module Danger
 4 |   # "abstract" CI class
 5 |   class CI
 6 |     attr_accessor :repo_slug, :pull_request_id, :repo_url, :supported_request_sources
 7 | 
 8 |     def self.inherited(child_class)
 9 |       available_ci_sources.add child_class
10 |       super
11 |     end
12 | 
13 |     def self.available_ci_sources
14 |       @available_ci_sources ||= Set.new
15 |     end
16 | 
17 |     def supported_request_sources
18 |       raise "CISource subclass must specify the supported request sources"
19 |     end
20 | 
21 |     def supports?(request_source)
22 |       supported_request_sources.include?(request_source)
23 |     end
24 | 
25 |     def self.validates_as_ci?(_env)
26 |       abort "You need to include a function for #{self} for validates_as_ci?"
27 |     end
28 | 
29 |     def self.validates_as_pr?(_env)
30 |       abort "You need to include a function for #{self} for validates_as_pr?"
31 |     end
32 | 
33 |     def initialize(_env)
34 |       raise "Subclass and overwrite initialize" if method(__method__).owner == Danger::CI
35 |     end
36 |   end
37 | end
38 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/circle_api.rb:
--------------------------------------------------------------------------------
 1 | require "faraday"
 2 | 
 3 | module Danger
 4 |   class CircleAPI
 5 |     # Determine if there's a PR attached to this commit,
 6 |     # and return a bool
 7 |     def pull_request?(env)
 8 |       url = pull_request_url(env)
 9 |       return !url.nil?
10 |     end
11 | 
12 |     # Determine if there's a PR attached to this commit,
13 |     # and return the url if so
14 |     def pull_request_url(env)
15 |       url = env["CI_PULL_REQUEST"]
16 | 
17 |       if url.nil? && !env["CIRCLE_PROJECT_USERNAME"].nil? && !env["CIRCLE_PROJECT_REPONAME"].nil?
18 |         repo_slug = env["CIRCLE_PROJECT_USERNAME"] + "/" + env["CIRCLE_PROJECT_REPONAME"]
19 |         if !env["CIRCLE_PR_NUMBER"].nil?
20 |           host = env["DANGER_GITHUB_HOST"] || "github.com"
21 |           url = "https://" + host + "/" + repo_slug + "/pull/" + env["CIRCLE_PR_NUMBER"]
22 |         else
23 |           token = env["DANGER_CIRCLE_CI_API_TOKEN"]
24 |           url = fetch_pull_request_url(repo_slug, env["CIRCLE_BUILD_NUM"], token)
25 |         end
26 |       end
27 |       url
28 |     end
29 | 
30 |     def client
31 |       @client ||= Faraday.new(url: "https://circleci.com/api/v1")
32 |     end
33 | 
34 |     # Ask the API if the commit is inside a PR
35 |     def fetch_pull_request_url(repo_slug, build_number, token)
36 |       build_json = fetch_build(repo_slug, build_number, token)
37 |       pull_requests = build_json[:pull_requests]
38 |       return nil unless pull_requests && pull_requests.first
39 | 
40 |       pull_requests.first[:url]
41 |     end
42 | 
43 |     # Make the API call, and parse the JSON
44 |     def fetch_build(repo_slug, build_number, token)
45 |       url = "project/#{repo_slug}/#{build_number}"
46 |       params = { "circle-token" => token }
47 |       response = client.get url, params, accept: "application/json"
48 |       JSON.parse(response.body, symbolize_names: true)
49 |     end
50 |   end
51 | end
52 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/cirrus.rb:
--------------------------------------------------------------------------------
 1 | require "danger/request_sources/github/github"
 2 | 
 3 | module Danger
 4 |   # ### CI Setup
 5 |   # You need to edit your `.cirrus.yml` to include `bundler exec danger`.
 6 |   #
 7 |   # Adding this to your `.cirrus.yml` allows Danger to fail your build, both on the Cirrus CI website and within your Pull Request.
 8 |   # With that set up, you can edit your task to add `bundler exec danger` in any script instruction.
 9 |   class Cirrus < CI
10 |     def self.validates_as_ci?(env)
11 |       env.key? "CIRRUS_CI"
12 |     end
13 | 
14 |     def self.validates_as_pr?(env)
15 |       exists = ["CIRRUS_PR", "CIRRUS_REPO_FULL_NAME"].all? { |x| env[x] && !env[x].empty? }
16 |       exists && env["CIRRUS_PR"].to_i > 0
17 |     end
18 | 
19 |     def supported_request_sources
20 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
21 |     end
22 | 
23 |     def initialize(env)
24 |       self.repo_slug = env["CIRRUS_REPO_FULL_NAME"]
25 |       if env["CIRRUS_PR"].to_i > 0
26 |         self.pull_request_id = env["CIRRUS_PR"]
27 |       end
28 |       self.repo_url = env["CIRRUS_GIT_CLONE_URL"]
29 |     end
30 |   end
31 | end
32 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/codefresh.rb:
--------------------------------------------------------------------------------
 1 | # https://semaphoreci.com/docs/available-environment-variables.html
 2 | require "danger/request_sources/github/github"
 3 | 
 4 | module Danger
 5 |   #  ### CI Setup
 6 |   #
 7 |   #  To set up Danger on Codefresh, create a freestyle step in your Codefresh yaml configuration:
 8 |   #
 9 |   #  ```yml
10 |   #  Danger:
11 |   #    title: Run Danger
12 |   #    image: alpine/bundle
13 |   #    working_directory: ${{main_clone}}
14 |   #    commands:
15 |   #      - bundle install --deployment
16 |   #      - bundle exec danger --verbose
17 |   #  ```
18 |   #
19 |   #  Don't forget to add the `DANGER_GITHUB_API_TOKEN` variable to your pipeline settings so that Danger can properly post comments to your pull request.
20 |   #
21 |   class Codefresh < CI
22 |     def self.validates_as_ci?(env)
23 |       env.key?("CF_BUILD_ID") && env.key?("CF_BUILD_URL")
24 |     end
25 | 
26 |     def self.validates_as_pr?(env)
27 |       return !env["CF_PULL_REQUEST_NUMBER"].to_s.empty?
28 |     end
29 | 
30 |     def supported_request_sources
31 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
32 |     end
33 | 
34 |     def self.slug_from(env)
35 |       return "" if env["CF_REPO_OWNER"].to_s.empty?
36 |       return "" if env["CF_REPO_NAME"].to_s.empty?
37 | 
38 |       "#{env['CF_REPO_OWNER']}/#{env['CF_REPO_NAME']}".downcase!
39 |     end
40 | 
41 |     def initialize(env)
42 |       self.repo_url = env["CF_COMMIT_URL"].to_s.gsub(%r{/commit.+$}, "")
43 |       self.repo_slug = self.class.slug_from(env)
44 |       self.pull_request_id = env["CF_PULL_REQUEST_NUMBER"]
45 |     end
46 |   end
47 | end
48 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/codemagic.rb:
--------------------------------------------------------------------------------
 1 | # https://docs.codemagic.io/building/environment-variables/
 2 | 
 3 | module Danger
 4 |   # ### CI Setup
 5 |   #
 6 |   # Add a script step to your workflow:
 7 |   #
 8 |   # ```
 9 |   # - name: Running Danger
10 |   #   script: |
11 |   #     bundle install
12 |   #     bundle exec danger
13 |   # ```
14 |   #
15 |   # ### Token Setup
16 |   #
17 |   # Add the following environment variables to your workflow's environment configuration.
18 |   # https://docs.codemagic.io/getting-started/yaml/
19 |   #
20 |   # #### GitHub
21 |   # Add the `DANGER_GITHUB_API_TOKEN` to your build user's ENV.
22 |   #
23 |   # #### GitLab
24 |   # Add the `DANGER_GITLAB_API_TOKEN` to your build user's ENV.
25 |   #
26 |   # #### Bitbucket Cloud
27 |   # Add the `DANGER_BITBUCKETSERVER_USERNAME`, `DANGER_BITBUCKETSERVER_PASSWORD`
28 |   # to your build user's ENV.
29 |   #
30 |   # #### Bitbucket server
31 |   # Add the `DANGER_BITBUCKETSERVER_USERNAME`, `DANGER_BITBUCKETSERVER_PASSWORD`
32 |   # and `DANGER_BITBUCKETSERVER_HOST` to your build user's ENV.
33 |   #
34 |   class Codemagic < CI
35 |     def self.validates_as_ci?(env)
36 |       env.key? "FCI_PROJECT_ID"
37 |     end
38 | 
39 |     def self.validates_as_pr?(env)
40 |       return !env["FCI_PULL_REQUEST_NUMBER"].to_s.empty?
41 |     end
42 | 
43 |     def supported_request_sources
44 |       @supported_request_sources ||= [
45 |         Danger::RequestSources::GitHub,
46 |         Danger::RequestSources::GitLab,
47 |         Danger::RequestSources::BitbucketServer,
48 |         Danger::RequestSources::BitbucketCloud
49 |       ]
50 |     end
51 | 
52 |     def initialize(env)
53 |       self.pull_request_id = env["FCI_PULL_REQUEST_NUMBER"]
54 |       self.repo_slug = env["FCI_REPO_SLUG"]
55 |       self.repo_url = GitRepo.new.origins # Codemagic doesn't provide a repo url env variable for n
56 |     end
57 |   end
58 | end
59 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/codeship.rb:
--------------------------------------------------------------------------------
 1 | # https://semaphoreci.com/docs/available-environment-variables.html
 2 | require "danger/request_sources/github/github"
 3 | 
 4 | module Danger
 5 |   # ### CI Setup
 6 |   #
 7 |   # In Codeship, go to your "Project Settings", then add `bundle exec danger` as a test step inside
 8 |   # one of your pipelines.
 9 |   #
10 |   # ### Token Setup
11 |   #
12 |   # Add your `DANGER_GITHUB_API_TOKEN` to "Environment" section in "Project Settings".
13 |   #
14 |   class Codeship < CI
15 |     def self.validates_as_ci?(env)
16 |       env["CI_NAME"] == "codeship"
17 |     end
18 | 
19 |     def self.validates_as_pr?(env)
20 |       return false unless env["CI_BRANCH"] && !env["CI_BRANCH"].empty?
21 | 
22 |       !pr_from_env(env).nil?
23 |     end
24 | 
25 |     def self.owner_for_github(env)
26 |       env["CI_REPO_NAME"].split("/").first
27 |     end
28 | 
29 |     # this is fairly hacky, see https://github.com/danger/danger/pull/892#issuecomment-329030616 for why
30 |     def self.pr_from_env(env)
31 |       Danger::RequestSources::GitHub.new(nil, env).get_pr_from_branch(env["CI_REPO_NAME"], env["CI_BRANCH"], owner_for_github(env))
32 |     end
33 | 
34 |     def supported_request_sources
35 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
36 |     end
37 | 
38 |     def initialize(env)
39 |       self.repo_slug = env["CI_REPO_NAME"]
40 |       self.pull_request_id = self.class.pr_from_env(env)
41 |       self.repo_url = GitRepo.new.origins
42 |     end
43 |   end
44 | end
45 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/concourse.rb:
--------------------------------------------------------------------------------
 1 | require "git"
 2 | require "danger/request_sources/local_only"
 3 | 
 4 | module Danger
 5 |   # Concourse CI Integration
 6 |   #
 7 |   # https://concourse-ci.org/
 8 |   #
 9 |   # ### CI Setup
10 |   #
11 |   # With Concourse, you run the docker images yourself, so you will want to add `yarn danger ci` within one of your build jobs.
12 |   #
13 |   #   ```shell
14 |   #    build:
15 |   #      image: golang
16 |   #        commands:
17 |   #          - ...
18 |   #          - yarn danger ci
19 |   #   ```
20 |   #
21 |   # ### Environment Variable Setup
22 |   #
23 |   # As this is self-hosted, you will need to add the `CONCOURSE` environment variable `export CONCOURSE=true` to your build environment,
24 |   # as well as setting environment variables for `PULL_REQUEST_ID` and `REPO_SLUG`. Assuming you are using the github pull request resource
25 |   # https://github.com/jtarchie/github-pullrequest-resource the id of the PR can be accessed from `git config --get pullrequest.id`.
26 |   #
27 |   # ### Token Setup
28 |   #
29 |   # Once again as this is self-hosted, you will need to add `DANGER_GITHUB_API_TOKEN` environment variable to the build environment.
30 |   # The suggested method of storing the token is within the vault - https://concourse-ci.org/creds.html
31 | 
32 |   class Concourse < CI
33 |     def self.validates_as_ci?(env)
34 |       env.key? "CONCOURSE"
35 |     end
36 | 
37 |     def self.validates_as_pr?(env)
38 |       exists = ["PULL_REQUEST_ID", "REPO_SLUG"].all? { |x| env[x] && !env[x].empty? }
39 |       exists && env["PULL_REQUEST_ID"].to_i > 0
40 |     end
41 | 
42 |     def supported_request_sources
43 |       @supported_request_sources ||= [
44 |         Danger::RequestSources::GitHub,
45 |         Danger::RequestSources::GitLab,
46 |         Danger::RequestSources::BitbucketServer,
47 |         Danger::RequestSources::BitbucketCloud
48 |       ]
49 |     end
50 | 
51 |     def initialize(env)
52 |       self.repo_slug = env["REPO_SLUG"]
53 | 
54 |       if env["PULL_REQUEST_ID"].to_i > 0
55 |         self.pull_request_id = env["PULL_REQUEST_ID"]
56 |       end
57 |       self.repo_url = GitRepo.new.origins
58 |     end
59 |   end
60 | end
61 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/custom_ci_with_github.rb:
--------------------------------------------------------------------------------
 1 | # frozen_string_literal: true
 2 | 
 3 | require "danger/request_sources/github/github"
 4 | 
 5 | module Danger
 6 |   # ### CI Setup
 7 |   #
 8 |   # Custom CI with GitHub
 9 |   #
10 |   # This CI source is for custom, most likely internal, CI systems that are use GitHub as source control.
11 |   # An example could be argo-workflows or tekton hosted in your own Kubernetes cluster.
12 |   #
13 |   # The following environment variables are required:
14 |   # - `CUSTOM_CI_WITH_GITHUB` - Set to any value to indicate that this is a custom CI with GitHub
15 |   #
16 |   # ### Token Setup
17 |   #
18 |   # #### GitHub
19 |   # As you own the setup, it's up to you to add the environment variable for the `DANGER_GITHUB_API_TOKEN`.
20 |   #
21 |   class CustomCIWithGithub < CI
22 |     def self.validates_as_ci?(env)
23 |       env.key? "CUSTOM_CI_WITH_GITHUB"
24 |     end
25 | 
26 |     def self.validates_as_pr?(env)
27 |       value = env["GITHUB_EVENT_NAME"]
28 |       ["pull_request", "pull_request_target"].include?(value)
29 |     end
30 | 
31 |     def supported_request_sources
32 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
33 |     end
34 | 
35 |     def initialize(env)
36 |       super
37 | 
38 |       self.repo_slug = env["GITHUB_REPOSITORY"]
39 |       pull_request_event = JSON.parse(File.read(env["GITHUB_EVENT_PATH"]))
40 |       self.pull_request_id = pull_request_event["number"]
41 |       self.repo_url = pull_request_event["repository"]["clone_url"]
42 | 
43 |       # if environment variable DANGER_GITHUB_API_TOKEN is not set, use env GITHUB_TOKEN
44 |       if (env.key? "CUSTOM_CI_WITH_GITHUB") && (!env.key? "DANGER_GITHUB_API_TOKEN")
45 |         env["DANGER_GITHUB_API_TOKEN"] = env["GITHUB_TOKEN"]
46 |       end
47 |     end
48 |   end
49 | end
50 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/dotci.rb:
--------------------------------------------------------------------------------
 1 | require "danger/request_sources/github/github"
 2 | 
 3 | module Danger
 4 |   # https://groupon.github.io/DotCi
 5 | 
 6 |   # ### CI Setup
 7 |   # DotCi is a layer on top of jenkins. So, if you're using DotCi, you're hosting your own environment.
 8 |   #
 9 |   # ### Token Setup
10 |   #
11 |   # #### GitHub
12 |   # As you own the machine, it's up to you to add the environment variable for the `DANGER_GITHUB_API_TOKEN`.
13 |   #
14 |   class DotCi < CI
15 |     def self.validates_as_ci?(env)
16 |       env.key? "DOTCI"
17 |     end
18 | 
19 |     def self.validates_as_pr?(env)
20 |       !env["DOTCI_PULL_REQUEST"].nil? && !env["DOTCI_PULL_REQUEST"].match(/^[0-9]+$/).nil?
21 |     end
22 | 
23 |     def supported_request_sources
24 |       @supported_request_sources ||= [
25 |         Danger::RequestSources::GitHub
26 |       ]
27 |     end
28 | 
29 |     def initialize(env)
30 |       self.repo_url = self.class.repo_url(env)
31 |       self.pull_request_id = self.class.pull_request_id(env)
32 |       repo_matches = self.repo_url.match(%r{([/:])([^/]+/[^/]+)$})
33 |       self.repo_slug = repo_matches[2].gsub(/\.git$/, "") unless repo_matches.nil?
34 |     end
35 | 
36 |     def self.pull_request_id(env)
37 |       env["DOTCI_PULL_REQUEST"]
38 |     end
39 | 
40 |     def self.repo_url(env)
41 |       if env["DOTCI_INSTALL_PACKAGES_GIT_CLONE_URL"]
42 |         env["DOTCI_INSTALL_PACKAGES_GIT_CLONE_URL"]
43 |       elsif env["DOTCI_DOCKER_COMPOSE_GIT_CLONE_URL"]
44 |         env["DOTCI_DOCKER_COMPOSE_GIT_CLONE_URL"]
45 |       else
46 |         env["GIT_URL"]
47 |       end
48 |     end
49 |   end
50 | end
51 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/drone.rb:
--------------------------------------------------------------------------------
 1 | # http://readme.drone.io/usage/variables/
 2 | require "danger/request_sources/github/github"
 3 | require "danger/request_sources/gitlab"
 4 | 
 5 | module Danger
 6 |   # ### CI Setup
 7 |   #
 8 |   # With Drone you run the docker images yourself, so you will want to add `bundle exec danger` at the end of
 9 |   # your `.drone.yml`.
10 |   #
11 |   #  ```shell
12 |   #   build:
13 |   #     image: golang
14 |   #     commands:
15 |   #       - ...
16 |   #       - bundle exec danger
17 |   #  ```
18 |   #
19 |   # ### Token Setup
20 |   #
21 |   # As this is self-hosted, you will need to expose the `DANGER_GITHUB_API_TOKEN` as a secret to your
22 |   # builds:
23 |   #
24 |   # Drone secrets: http://readme.drone.io/usage/secret-guide/
25 |   # NOTE: This is a new syntax in DroneCI 0.6+
26 |   #
27 |   # ```yml
28 |   #   build:
29 |   #     image: golang
30 |   #     secrets:
31 |   #       - DANGER_GITHUB_API_TOKEN
32 |   #     commands:
33 |   #       - ...
34 |   #       - bundle exec danger
35 |   # ```
36 |   class Drone < CI
37 |     def self.validates_as_ci?(env)
38 |       validates_as_ci_post_06?(env) or validates_as_ci_pre_06?(env)
39 |     end
40 | 
41 |     def self.validates_as_pr?(env)
42 |       env["DRONE_PULL_REQUEST"].to_i > 0
43 |     end
44 | 
45 |     def supported_request_sources
46 |       @supported_request_sources ||= [Danger::RequestSources::GitHub, Danger::RequestSources::GitLab]
47 |     end
48 | 
49 |     def initialize(env)
50 |       if self.class.validates_as_ci_post_06?(env)
51 |         self.repo_slug = "#{env['DRONE_REPO_OWNER']}/#{env['DRONE_REPO_NAME']}"
52 |         self.repo_url = env["DRONE_REPO_LINK"] if self.class.validates_as_ci_post_06?(env)
53 |       elsif self.class.validates_as_ci_pre_06?(env)
54 |         self.repo_slug = env["DRONE_REPO"]
55 |         self.repo_url = GitRepo.new.origins
56 |       end
57 | 
58 |       self.pull_request_id = env["DRONE_PULL_REQUEST"]
59 |     end
60 | 
61 |     # Check if this build is valid for CI with drone 0.6 or later
62 |     def self.validates_as_ci_post_06?(env)
63 |       env.key? "DRONE_REPO_OWNER" and env.key? "DRONE_REPO_NAME"
64 |     end
65 | 
66 |     # Checks if this build is valid for CI with drone 0.5 or earlier
67 |     def self.validates_as_ci_pre_06?(env)
68 |       env.key? "DRONE_REPO"
69 |     end
70 |   end
71 | end
72 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/github_actions.rb:
--------------------------------------------------------------------------------
 1 | require "danger/request_sources/github/github"
 2 | 
 3 | module Danger
 4 |   # ### CI Setup
 5 |   #
 6 |   # You can use `danger/danger` Action in your `.github/workflows/xxx.yml`.
 7 |   # And so, you can use GITHUB_TOKEN secret as `DANGER_GITHUB_API_TOKEN` environment variable.
 8 |   #
 9 |   #  ```yml
10 |   #  ...
11 |   #    steps:
12 |   #      - uses: actions/checkout@v3
13 |   #      - uses: danger/danger@master
14 |   #        env:
15 |   #          DANGER_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
16 |   #  ```
17 |   #
18 |   class GitHubActions < CI
19 |     def self.validates_as_ci?(env)
20 |       env.key? "GITHUB_ACTION"
21 |     end
22 | 
23 |     def self.validates_as_pr?(env)
24 |       value = env["GITHUB_EVENT_NAME"]
25 |       ["pull_request", "pull_request_target"].include?(value)
26 |     end
27 | 
28 |     def supported_request_sources
29 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
30 |     end
31 | 
32 |     def initialize(env)
33 |       self.repo_slug = env["GITHUB_REPOSITORY"]
34 |       pull_request_event = JSON.parse(File.read(env["GITHUB_EVENT_PATH"]))
35 |       self.pull_request_id = pull_request_event["number"]
36 |       self.repo_url = pull_request_event["repository"]["clone_url"]
37 | 
38 |       # if environment variable DANGER_GITHUB_API_TOKEN is not set, use env GITHUB_TOKEN
39 |       if (env.key? "GITHUB_ACTION") && (!env.key? "DANGER_GITHUB_API_TOKEN")
40 |         env["DANGER_GITHUB_API_TOKEN"] = env["GITHUB_TOKEN"]
41 |       end
42 |     end
43 |   end
44 | end
45 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/local_only_git_repo.rb:
--------------------------------------------------------------------------------
 1 | # frozen_string_literal: true
 2 | 
 3 | require "git"
 4 | require "danger/request_sources/local_only"
 5 | 
 6 | module Danger
 7 |   # ### CI Setup
 8 |   #
 9 |   # For setting up LocalOnlyGitRepo there is not much needed. Either `--base` and `--head` need to be specified or
10 |   # origin/master is expected for base and HEAD for head
11 |   #
12 |   class LocalOnlyGitRepo < CI
13 |     attr_accessor :base_commit, :head_commit
14 | 
15 |     HEAD_VAR = "DANGER_LOCAL_HEAD"
16 |     BASE_VAR = "DANGER_LOCAL_BASE"
17 | 
18 |     def self.validates_as_ci?(env)
19 |       env.key? "DANGER_USE_LOCAL_ONLY_GIT"
20 |     end
21 | 
22 |     def self.validates_as_pr?(_env)
23 |       false
24 |     end
25 | 
26 |     def git
27 |       @git ||= GitRepo.new
28 |     end
29 | 
30 |     def run_git(command)
31 |       git.exec(command).encode("UTF-8", "binary", invalid: :replace, undef: :replace, replace: "")
32 |     end
33 | 
34 |     def supported_request_sources
35 |       @supported_request_sources ||= [Danger::RequestSources::LocalOnly]
36 |     end
37 | 
38 |     def initialize(env = {})
39 |       # expects --base/--head specified OR origin/master to be base and HEAD head
40 |       self.base_commit = env[BASE_VAR] || run_git("rev-parse --abbrev-ref origin/master")
41 |       self.head_commit = env[HEAD_VAR] || run_git("rev-parse --abbrev-ref HEAD")
42 |     end
43 |   end
44 | end
45 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/screwdriver.rb:
--------------------------------------------------------------------------------
 1 | # http://screwdriver.cd
 2 | # https://docs.screwdriver.cd/user-guide/environment-variables
 3 | require "danger/request_sources/github/github"
 4 | 
 5 | module Danger
 6 |   # ### CI Setup
 7 |   #
 8 |   # Install dependencies and add a danger step to your screwdriver.yaml:
 9 |   #
10 |   # ```yml
11 |   # jobs:
12 |   #   danger:
13 |   #     requires: [~pr, ~commit]
14 |   #     steps:
15 |   #       - setup: bundle install --path vendor
16 |   #       - danger: bundle exec danger
17 |   #     secrets:
18 |   #       - DANGER_GITHUB_API_TOKEN
19 |   # ```
20 |   #
21 |   # ### Token Setup
22 |   #
23 |   # Add the `DANGER_GITHUB_API_TOKEN` to your pipeline env as a
24 |   # [build secret](https://docs.screwdriver.cd/user-guide/configuration/secrets)
25 |   #
26 |   class Screwdriver < CI
27 |     def self.validates_as_ci?(env)
28 |       env.key? "SCREWDRIVER"
29 |     end
30 | 
31 |     def self.validates_as_pr?(env)
32 |       exists = ["SD_PULL_REQUEST", "SCM_URL"].all? { |x| env[x] && !env[x].empty? }
33 |       exists && env["SD_PULL_REQUEST"].to_i > 0
34 |     end
35 | 
36 |     def supported_request_sources
37 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
38 |     end
39 | 
40 |     def initialize(env)
41 |       self.repo_slug = env["SCM_URL"].split(":").last.gsub(".git", "").split("#", 2).first
42 |       self.repo_url = env["SCM_URL"].split("#", 2).first
43 |       if env["SD_PULL_REQUEST"].to_i > 0
44 |         self.pull_request_id = env["SD_PULL_REQUEST"]
45 |       end
46 |     end
47 |   end
48 | end
49 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/semaphore.rb:
--------------------------------------------------------------------------------
 1 | # https://docs.semaphoreci.com/article/12-environment-variables
 2 | require "danger/request_sources/github/github"
 3 | 
 4 | module Danger
 5 |   # ### CI Setup
 6 |   #
 7 |   # For Semaphore you will want to go to the settings page of the project. Inside "Build Settings"
 8 |   # you should add `bundle exec danger` to the Setup thread. Note that Semaphore only provides
 9 |   # the build environment variables necessary for Danger on PRs across forks.
10 |   #
11 |   # ### Token Setup
12 |   #
13 |   # You can add your `DANGER_GITHUB_API_TOKEN` inside the "Environment Variables" section in the settings.
14 |   #
15 |   class Semaphore < CI
16 |     def self.validates_as_ci?(env)
17 |       env.key? "SEMAPHORE"
18 |     end
19 | 
20 |     def self.validates_as_pr?(env)
21 |       one = ["SEMAPHORE_REPO_SLUG", "PULL_REQUEST_NUMBER"].all? { |x| env[x] && !env[x].empty? }
22 |       two = ["SEMAPHORE_GIT_REPO_SLUG", "SEMAPHORE_GIT_PR_NUMBER"].all? { |x| env[x] && !env[x].empty? }
23 | 
24 |       one || two
25 |     end
26 | 
27 |     def supported_request_sources
28 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
29 |     end
30 | 
31 |     def initialize(env)
32 |       self.repo_slug = env["SEMAPHORE_GIT_REPO_SLUG"] || env["SEMAPHORE_REPO_SLUG"]
33 |       self.pull_request_id = env["SEMAPHORE_GIT_PR_NUMBER"] || env["PULL_REQUEST_NUMBER"]
34 |       self.repo_url = env["SEMAPHORE_GIT_URL"] || GitRepo.new.origins
35 |     end
36 |   end
37 | end
38 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/support/commits.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Commits
 3 |     def initialize(base_head)
 4 |       @base_head = base_head.strip.split(" ".freeze)
 5 |     end
 6 | 
 7 |     def base
 8 |       base_head.first
 9 |     end
10 | 
11 |     def head
12 |       base_head.last
13 |     end
14 | 
15 |     private
16 | 
17 |     attr_reader :base_head
18 |   end
19 | end
20 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/support/find_repo_info_from_logs.rb:
--------------------------------------------------------------------------------
 1 | require "danger/ci_source/support/repo_info"
 2 | 
 3 | module Danger
 4 |   class FindRepoInfoFromLogs
 5 |     def initialize(github_host, remote_logs)
 6 |       @github_host = github_host
 7 |       @remote_logs = remote_logs
 8 |     end
 9 | 
10 |     def call
11 |       matched = remote.match(regexp)
12 | 
13 |       if matched
14 |         RepoInfo.new(matched["repo_slug"], nil)
15 |       end
16 |     end
17 | 
18 |     private
19 | 
20 |     attr_reader :remote_logs, :github_host
21 | 
22 |     def remote
23 |       remote_logs.lines.grep(/Fetch URL/)[0].split(": ".freeze, 2)[1]
24 |     end
25 | 
26 |     def regexp
27 |       %r{
28 |         #{Regexp.escape(github_host)}
29 |         (:|/|(:/))
30 |         (?<repo_slug>[^/]+/.+?)
31 |         (?:\.git)?$
32 |       }x
33 |     end
34 |   end
35 | end
36 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/support/find_repo_info_from_url.rb:
--------------------------------------------------------------------------------
 1 | require "danger/ci_source/support/repo_info"
 2 | 
 3 | module Danger
 4 |   class FindRepoInfoFromURL
 5 |     REGEXP = %r{
 6 |       ://[^/]+/
 7 |       (([^/]+/){1,2}_git/)?
 8 |       (?<slug>[^/]+(/[^/]+){0,2})
 9 |       (/(pull|pullrequest|merge_requests|pull-requests)/)
10 |       (?<id>\d+)
11 |     }x.freeze
12 | 
13 |     # Regex used to extract info from Bitbucket server URLs, as they use a quite different format
14 |     REGEXPBB = %r{
15 |       (?:[/:])projects
16 |       /([^/.]+)
17 |       /repos/([^/.]+)
18 |       /pull-requests
19 |       /(\d+)
20 |     }x.freeze
21 | 
22 |     def initialize(url)
23 |       @url = url
24 |     end
25 | 
26 |     def call
27 |       matched = url.match(REGEXPBB)
28 | 
29 |       if matched
30 |         RepoInfo.new("#{matched[1]}/#{matched[2]}", matched[3])
31 |       else
32 |         matched = url.match(REGEXP)
33 |         if matched
34 |           # Clean up the slug to remove any trailing dashes or slashes that might be part of the GitLab URL format
35 |           clean_slug = matched[:slug].gsub(%r{[-/]+$}, "")
36 |           RepoInfo.new(clean_slug, matched[:id])
37 |         end
38 |       end
39 |     end
40 | 
41 |     private
42 | 
43 |     attr_reader :url
44 |   end
45 | end
46 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/support/local_pull_request.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class LocalPullRequest
 3 |     attr_reader :pull_request_id, :sha
 4 | 
 5 |     def initialize(log_line)
 6 |       @pull_request_id = log_line.match(/#(?<id>[0-9]+)/)[:id]
 7 |       @sha = log_line.split(" ".freeze).first
 8 |     end
 9 | 
10 |     def valid?
11 |       pull_request_id && sha
12 |     end
13 |   end
14 | end
15 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/support/no_pull_request.rb:
--------------------------------------------------------------------------------
1 | module Danger
2 |   class NoPullRequest
3 |     def valid?
4 |       false
5 |     end
6 |   end
7 | end
8 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/support/no_repo_info.rb:
--------------------------------------------------------------------------------
1 | module Danger
2 |   class NoRepoInfo
3 |     attr_reader :slug, :id
4 |   end
5 | end
6 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/support/remote_pull_request.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class RemotePullRequest
 3 |     attr_reader :pull_request_id, :sha, :head, :base
 4 | 
 5 |     def initialize(pull_request_id, head, base)
 6 |       @pull_request_id = pull_request_id
 7 |       @head = head
 8 |       @base = base
 9 |     end
10 | 
11 |     def valid?
12 |       pull_request_id && head && base
13 |     end
14 |   end
15 | end
16 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/support/repo_info.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class RepoInfo
 3 |     attr_reader :slug, :id
 4 | 
 5 |     def initialize(slug, id)
 6 |       @slug = slug
 7 |       @id = id
 8 |     end
 9 |   end
10 | end
11 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/surf.rb:
--------------------------------------------------------------------------------
 1 | # http://github.com/surf-build/surf
 2 | require "danger/request_sources/github/github"
 3 | 
 4 | module Danger
 5 |   # ### CI Setup
 6 |   #
 7 |   # You want to add `bundle exec danger` to your `build.sh` file to run  Danger at the
 8 |   # end of your build.
 9 |   #
10 |   # ### Token Setup
11 |   #
12 |   # As this is self-hosted, you will need to add the `DANGER_GITHUB_API_TOKEN` to your build user's ENV. The alternative
13 |   # is to pass in the token as a prefix to the command `DANGER_GITHUB_API_TOKEN="123" bundle exec danger`.
14 |   #
15 |   class Surf < CI
16 |     def self.validates_as_ci?(env)
17 |       return ["SURF_REPO", "SURF_NWO"].all? { |x| env[x] && !env[x].empty? }
18 |     end
19 | 
20 |     def self.validates_as_pr?(env)
21 |       validates_as_ci?(env)
22 |     end
23 | 
24 |     def supported_request_sources
25 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
26 |     end
27 | 
28 |     def initialize(env)
29 |       self.repo_slug = env["SURF_NWO"]
30 |       if env["SURF_PR_NUM"].to_i > 0
31 |         self.pull_request_id = env["SURF_PR_NUM"]
32 |       end
33 | 
34 |       self.repo_url = env["SURF_REPO"]
35 |     end
36 |   end
37 | end
38 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/travis.rb:
--------------------------------------------------------------------------------
 1 | # http://docs.travis-ci.com/user/osx-ci-environment/
 2 | # http://docs.travis-ci.com/user/environment-variables/
 3 | require "danger/request_sources/github/github"
 4 | 
 5 | module Danger
 6 |   # ### CI Setup
 7 |   # You need to edit your `.travis.yml` to include `bundle exec danger`. If you already have
 8 |   # a `script:` section then we recommend adding this command at the end of the script step: `- bundle exec danger`.
 9 |   #
10 |   #  Otherwise, add a `before_script` step to the root of the `.travis.yml` with `bundle exec danger`
11 |   #
12 |   #  ```ruby
13 |   #    before_script:
14 |   #      - bundle exec danger
15 |   #  ```
16 |   #
17 |   # Adding this to your `.travis.yml` allows Danger to fail your build, both on the TravisCI website and within your Pull Request.
18 |   # With that set up, you can edit your job to add `bundle exec danger` at the build action.
19 |   #
20 |   # _Note:_ Travis CI defaults to using an older version of Ruby, so you may need to add `rvm: 2.0.0` to the root your `.travis.yml`.
21 |   #
22 |   # ### Token Setup
23 |   #
24 |   # You need to add the `DANGER_GITHUB_API_TOKEN` environment variable, to do this,
25 |   # go to your repo's settings, which should look like: `https://travis-ci.org/[user]/[repo]/settings`.
26 |   #
27 |   # If you have an open source project, you should ensure "Display value in build log" enabled, so that PRs from forks work.
28 |   #
29 |   class Travis < CI
30 |     def self.validates_as_ci?(env)
31 |       env.key? "HAS_JOSH_K_SEAL_OF_APPROVAL"
32 |     end
33 | 
34 |     def self.validates_as_pr?(env)
35 |       exists = ["TRAVIS_PULL_REQUEST", "TRAVIS_REPO_SLUG"].all? { |x| env[x] && !env[x].empty? }
36 |       exists && env["TRAVIS_PULL_REQUEST"].to_i > 0
37 |     end
38 | 
39 |     def supported_request_sources
40 |       @supported_request_sources ||= [Danger::RequestSources::GitHub]
41 |     end
42 | 
43 |     def initialize(env)
44 |       self.repo_slug = env["TRAVIS_REPO_SLUG"]
45 |       if env["TRAVIS_PULL_REQUEST"].to_i > 0
46 |         self.pull_request_id = env["TRAVIS_PULL_REQUEST"]
47 |       end
48 |       self.repo_url = GitRepo.new.origins # Travis doesn't provide a repo url env variable :/
49 |     end
50 |   end
51 | end
52 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/xcode_cloud.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   # ### CI Setup
 3 |   #
 4 |   # In order to work with Xcode Cloud and Danger, you will need to add `bundle exec danger` to
 5 |   # the `ci_scripts/ci_post_xcodebuild.sh` (Xcode Cloud's expected filename for a post-action build script).
 6 |   # More details and documentation on Xcode Cloud configuration can be found [here](https://developer.apple.com/documentation/xcode/writing-custom-build-scripts).
 7 |   #
 8 |   # ### Token Setup
 9 |   #
10 |   # You will need to add the `DANGER_GITHUB_API_TOKEN` to your build environment.
11 |   # If running on GitHub Enterprise, make sure you also set the expected values for
12 |   # both `DANGER_GITHUB_API_HOST` and `DANGER_GITHUB_HOST`.
13 |   #
14 |   class XcodeCloud < CI
15 |     def self.validates_as_ci?(env)
16 |       env.key? "CI_XCODEBUILD_ACTION"
17 |     end
18 | 
19 |     def self.validates_as_pr?(env)
20 |       env.key? "CI_PULL_REQUEST_NUMBER"
21 |     end
22 | 
23 |     def supported_request_sources
24 |       @supported_request_sources ||= [
25 |         Danger::RequestSources::GitHub,
26 |         Danger::RequestSources::GitLab,
27 |         Danger::RequestSources::BitbucketCloud,
28 |         Danger::RequestSources::BitbucketServer
29 |       ]
30 |     end
31 | 
32 |     def initialize(env)
33 |       self.repo_slug = env["CI_PULL_REQUEST_SOURCE_REPO"]
34 |       self.pull_request_id = env["CI_PULL_REQUEST_NUMBER"]
35 |       self.repo_url = env["CI_PULL_REQUEST_HTML_URL"]
36 |     end
37 |   end
38 | end
39 | 


--------------------------------------------------------------------------------
/lib/danger/ci_source/xcode_server.rb:
--------------------------------------------------------------------------------
 1 | # Following the advice from @czechboy0 https://github.com/danger/danger/issues/171
 2 | # https://github.com/czechboy0/Buildasaur
 3 | require "danger/request_sources/github/github"
 4 | 
 5 | module Danger
 6 |   # ### CI Setup
 7 |   #
 8 |   # If you're bold enough to use Xcode Bots. You will need to use [Buildasaur](https://github.com/czechboy0/Buildasaur)
 9 |   # in order to work with Danger. This will set up your build environment for you, as the name of the bot contains all
10 |   # of the environment variables that Danger needs to work.
11 |   #
12 |   # With Buildasaur set up, you can edit your job to add `bundle exec danger` as a post-action build script.
13 |   #
14 |   # ### Token Setup
15 |   #
16 |   # As this is self-hosted, you will need to add the `DANGER_GITHUB_API_TOKEN` to your build user's ENV. The alternative
17 |   # is to pass in the token as a prefix to the command `DANGER_GITHUB_API_TOKEN="123" bundle exec danger`.`.
18 |   #
19 |   class XcodeServer < CI
20 |     def self.validates_as_ci?(env)
21 |       env.key? "XCS_BOT_NAME"
22 |     end
23 | 
24 |     def self.validates_as_pr?(env)
25 |       value = env["XCS_BOT_NAME"]
26 |       !value.nil? && value.include?("BuildaBot")
27 |     end
28 | 
29 |     def supported_request_sources
30 |       @supported_request_sources ||= [
31 |         Danger::RequestSources::GitHub,
32 |         Danger::RequestSources::BitbucketServer,
33 |         Danger::RequestSources::BitbucketCloud
34 |       ]
35 |     end
36 | 
37 |     def initialize(env)
38 |       bot_name = env["XCS_BOT_NAME"]
39 |       return if bot_name.nil?
40 | 
41 |       repo_matches = bot_name.match(/\[(.+)\]/)
42 |       self.repo_slug = repo_matches[1] unless repo_matches.nil?
43 |       pull_request_id_matches = bot_name.match(/#(\d+)/)
44 |       self.pull_request_id = pull_request_id_matches[1] unless pull_request_id_matches.nil?
45 |       self.repo_url = GitRepo.new.origins # Xcode Server doesn't provide a repo url env variable :/
46 |     end
47 |   end
48 | end
49 | 


--------------------------------------------------------------------------------
/lib/danger/clients/rubygems_client.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class RubyGemsClient
 3 |     API_URL = "https://rubygems.org/api/v1/versions/danger/latest.json".freeze
 4 |     DUMMY_VERSION = "0.0.0".freeze
 5 | 
 6 |     def self.latest_danger_version
 7 |       require "json"
 8 |       json = JSON.parse(Faraday.get(API_URL).body)
 9 |       json.fetch("version") { DUMMY_VERSION }
10 |     rescue StandardError => _e
11 |       DUMMY_VERSION
12 |     end
13 |   end
14 | end
15 | 


--------------------------------------------------------------------------------
/lib/danger/commands/dangerfile/gem.rb:
--------------------------------------------------------------------------------
 1 | require "claide_plugin"
 2 | require "danger/commands/dangerfile/init"
 3 | 
 4 | module Danger
 5 |   class DangerfileGem < DangerfileCommand
 6 |     self.summary = "Create a gem-based Dangerfile quickly."
 7 |     def self.description
 8 |       <<-DESC
 9 |                 Creates a scaffold for the development of a new gem based Dangerfile
10 |                 named `NAME` according to the best practices.
11 |       DESC
12 |     end
13 |     self.command = "gem"
14 |     self.arguments = [
15 |       CLAide::Argument.new("NAME", true)
16 |     ]
17 | 
18 |     def initialize(argv)
19 |       @name = argv.shift_argument
20 |       prefix = "dangerfile" + "-"
21 |       unless @name.nil? || @name.empty? || @name.start_with?(prefix)
22 |         @name = prefix + @name.dup
23 |       end
24 |       @template_url = argv.shift_argument
25 |       super
26 |     end
27 | 
28 |     def validate!
29 |       super
30 |       if @name.nil? || @name.empty?
31 |         help! "A name for the plugin is required."
32 |       end
33 | 
34 |       help! "The plugin name cannot contain spaces." if @name =~ /\s/
35 |     end
36 | 
37 |     def run
38 |       runner = CLAide::TemplateRunner.new(@name, "https://github.com/danger/dangerfile-gem-template")
39 |       runner.clone_template
40 |       runner.configure_template
41 |     end
42 |   end
43 | end
44 | 


--------------------------------------------------------------------------------
/lib/danger/commands/dangerfile/init.rb:
--------------------------------------------------------------------------------
 1 | require "danger/danger_core/dangerfile_generator"
 2 | 
 3 | # Mainly so we can have a nice structure for commands
 4 | 
 5 | module Danger
 6 |   class DangerfileCommand < Runner
 7 |     self.summary = "Easily create your Dangerfiles."
 8 |     self.command = "dangerfile"
 9 | 
10 |     self.abstract_command = true
11 |     def self.options
12 |       []
13 |     end
14 |   end
15 | end
16 | 
17 | # Just a less verbose way of doing the Dangerfile from `danger init`.
18 | 
19 | module Danger
20 |   class DangerfileInit < DangerfileCommand
21 |     self.summary = "Create an example Dangerfile."
22 |     self.command = "init"
23 | 
24 |     def run
25 |       content = DangerfileGenerator.create_dangerfile(".", cork)
26 |       File.write("Dangerfile", content)
27 |       cork.puts "Created" + "./Dangerfile".green
28 |     end
29 |   end
30 | end
31 | 


--------------------------------------------------------------------------------
/lib/danger/commands/dry_run.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/local_helpers/pry_setup"
 2 | require "fileutils"
 3 | 
 4 | module Danger
 5 |   class DryRun < Runner
 6 |     self.summary = "Dry-Run the Dangerfile locally, so you could check some violations before sending real PR/MR."
 7 |     self.command = "dry_run"
 8 | 
 9 |     def self.options
10 |       [
11 |         ["--pry", "Drop into a Pry shell after evaluating the Dangerfile."]
12 |       ]
13 |     end
14 | 
15 |     def initialize(argv)
16 |       show_help = true if argv.arguments == ["-h"]
17 | 
18 |       # Currently CLAide doesn't support short option like -h https://github.com/CocoaPods/CLAide/pull/60
19 |       # when user pass in -h, mimic the behavior of passing in --help.
20 |       argv = CLAide::ARGV.new ["--help"] if show_help
21 | 
22 |       super
23 | 
24 |       if argv.flag?("pry", false)
25 |         @dangerfile_path = PrySetup.new(cork).setup_pry(@dangerfile_path, DryRun.command)
26 |       end
27 |     end
28 | 
29 |     def validate!
30 |       super
31 |       unless @dangerfile_path
32 |         help! "Could not find a Dangerfile."
33 |       end
34 |     end
35 | 
36 |     def run
37 |       ENV["DANGER_USE_LOCAL_ONLY_GIT"] = "YES"
38 |       ENV["DANGER_LOCAL_HEAD"] = @head if @head
39 |       ENV["DANGER_LOCAL_BASE"] = @base if @base
40 | 
41 |       env = EnvironmentManager.new(ENV, cork)
42 |       dm = Dangerfile.new(env, cork)
43 | 
44 |       exit 1 if dm.run(
45 |         Danger::EnvironmentManager.danger_base_branch,
46 |         Danger::EnvironmentManager.danger_head_branch,
47 |         @dangerfile_path,
48 |         nil,
49 |         nil,
50 |         nil
51 |       )
52 |     end
53 |   end
54 | end
55 | 


--------------------------------------------------------------------------------
/lib/danger/commands/init_helpers/interviewer.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Interviewer
 3 |     attr_accessor :no_delay, :no_waiting, :ui
 4 | 
 5 |     def initialize(cork_board)
 6 |       @ui = cork_board
 7 |     end
 8 | 
 9 |     def show_prompt
10 |       ui.print "> ".bold.green
11 |     end
12 | 
13 |     def yellow_bang
14 |       "! ".yellow
15 |     end
16 | 
17 |     def green_bang
18 |       "! ".green
19 |     end
20 | 
21 |     def red_bang
22 |       "! ".red
23 |     end
24 | 
25 |     def say(output)
26 |       ui.puts output
27 |     end
28 | 
29 |     def header(title)
30 |       say title.yellow
31 |       say ""
32 |       pause 0.6
33 |     end
34 | 
35 |     def link(url)
36 |       say " -> " + url.underlined + "\n"
37 |     end
38 | 
39 |     def pause(time)
40 |       sleep(time) unless @no_waiting
41 |     end
42 | 
43 |     def wait_for_return
44 |       STDOUT.flush
45 |       STDIN.gets unless @no_delay
46 |       ui.puts
47 |     end
48 | 
49 |     def run_command(command, output_command = nil)
50 |       output_command ||= command
51 |       ui.puts "  " + output_command.magenta
52 |       system command
53 |     end
54 | 
55 |     def ask_with_answers(question, possible_answers)
56 |       ui.print "\n#{question}? ["
57 | 
58 |       print_info = proc do
59 |         possible_answers.each_with_index do |answer, i|
60 |           the_answer = i.zero? ? answer.underlined : answer
61 |           ui.print " " + the_answer
62 |           ui.print(" /") if i != possible_answers.length - 1
63 |         end
64 |         ui.print " ]\n"
65 |       end
66 |       print_info.call
67 | 
68 |       answer = ""
69 | 
70 |       loop do
71 |         show_prompt
72 |         answer = @no_waiting ? possible_answers[0].downcase : STDIN.gets.downcase.chomp
73 | 
74 |         answer = "yes" if answer == "y"
75 |         answer = "no" if answer == "n"
76 | 
77 |         # default to first answer
78 |         if answer == ""
79 |           answer = possible_answers[0].downcase
80 |           ui.puts "Using: " + answer.yellow
81 |         end
82 | 
83 |         break if possible_answers.map(&:downcase).include? answer
84 | 
85 |         ui.print "\nPossible answers are ["
86 |         print_info.call
87 |       end
88 | 
89 |       answer
90 |     end
91 |   end
92 | end
93 | 


--------------------------------------------------------------------------------
/lib/danger/commands/local_helpers/http_cache.rb:
--------------------------------------------------------------------------------
 1 | require "pstore"
 2 | 
 3 | module Danger
 4 |   class HTTPCache
 5 |     attr_reader :expires_in
 6 | 
 7 |     def initialize(cache_file = nil, options = {})
 8 |       File.delete(cache_file) if options[:clear_cache]
 9 |       @store = PStore.new(cache_file)
10 |       @expires_in = options[:expires_in] || 300 # 5 minutes
11 |     end
12 | 
13 |     def read(key)
14 |       @store.transaction do
15 |         entry = @store[key]
16 |         return nil unless entry
17 |         return entry[:value] unless entry_has_expired(entry, @expires_in)
18 | 
19 |         @store.delete key
20 |         return nil
21 |       end
22 |     end
23 | 
24 |     def delete(key)
25 |       @store.transaction { @store.delete key }
26 |     end
27 | 
28 |     def write(key, value)
29 |       @store.transaction do
30 |         @store[key] = { updated_at: Time.now.to_i, value: value }
31 |       end
32 |     end
33 | 
34 |     def entry_has_expired(entry, ttl)
35 |       Time.now.to_i > entry[:updated_at].to_i + ttl.to_i
36 |     end
37 |   end
38 | end
39 | 


--------------------------------------------------------------------------------
/lib/danger/commands/local_helpers/local_setup.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class LocalSetup
 3 |     attr_reader :dm, :cork
 4 | 
 5 |     def initialize(dangerfile, cork)
 6 |       @dm = dangerfile
 7 |       @cork = cork
 8 |     end
 9 | 
10 |     def setup(verbose: false)
11 |       source = dm.env.ci_source
12 |       if source.nil? or source.repo_slug.empty?
13 |         cork.puts "danger local failed because it only works with GitHub and Bitbucket server projects at the moment. Sorry.".red
14 |         exit 0
15 |       end
16 |       gh = dm.env.request_source
17 |       # We can use tokenless here, as it's running on someone's computer
18 |       # and is IP locked, as opposed to on the CI. Only for Github PRs
19 |       if gh.instance_of? Danger::RequestSources::GitHub
20 |         gh.support_tokenless_auth = true
21 |       end
22 | 
23 |       if gh.instance_of? Danger::RequestSources::BitbucketServer
24 |         cork.puts "Running your Dangerfile against this PR - #{gh.host}/projects/#{source.repo_slug.split('/').first}/repos/#{source.repo_slug.split('/').last}/pull-requests/#{source.pull_request_id}"
25 |       elsif gh.instance_of? Danger::RequestSources::VSTS
26 |         cork.puts "Running your Dangerfile against this PR - #{gh.client.pr_api_endpoint}"
27 |       else
28 |         cork.puts "Running your Dangerfile against this PR - https://#{gh.host}/#{source.repo_slug}/pull/#{source.pull_request_id}"
29 |       end
30 | 
31 |       unless verbose
32 |         cork.puts "Turning on --verbose"
33 |         dm.verbose = true
34 |       end
35 | 
36 |       cork.puts
37 | 
38 |       begin
39 |         gh.fetch_details
40 |       rescue Octokit::NotFound
41 |         cork.puts "Local repository was not found on GitHub. If you're trying to test a private repository please provide a valid API token through " + "DANGER_GITHUB_API_TOKEN".yellow + " environment variable."
42 |         return
43 |       end
44 | 
45 |       yield
46 |     end
47 |   end
48 | end
49 | 


--------------------------------------------------------------------------------
/lib/danger/commands/local_helpers/pry_setup.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class PrySetup
 3 |     def initialize(cork)
 4 |       @cork = cork
 5 |     end
 6 | 
 7 |     def setup_pry(dangerfile_path, command)
 8 |       return dangerfile_path if dangerfile_path.empty?
 9 | 
10 |       validate_pry_available(command)
11 |       FileUtils.cp dangerfile_path, DANGERFILE_COPY
12 |       File.open(DANGERFILE_COPY, "a") do |f|
13 |         f.write("\nbinding.pry; File.delete(\"#{DANGERFILE_COPY}\")")
14 |       end
15 |       DANGERFILE_COPY
16 |     end
17 | 
18 |     private
19 | 
20 |     attr_reader :cork
21 | 
22 |     DANGERFILE_COPY = "_Dangerfile.tmp".freeze
23 | 
24 |     def validate_pry_available(command)
25 |       Kernel.require "pry"
26 |     rescue LoadError
27 |       cork.warn "Pry was not found, and is required for 'danger #{command} --pry'."
28 |       cork.print_warnings
29 |       abort
30 |     end
31 |   end
32 | end
33 | 


--------------------------------------------------------------------------------
/lib/danger/commands/plugins/plugin_json.rb:
--------------------------------------------------------------------------------
 1 | require "danger/plugin_support/plugin_parser"
 2 | require "danger/plugin_support/plugin_file_resolver"
 3 | 
 4 | module Danger
 5 |   class PluginJSON < CLAide::Command::Plugins
 6 |     self.summary = "Lint plugins from files, gems or the current folder. Outputs JSON array representation of Plugins on success."
 7 |     self.command = "json"
 8 | 
 9 |     attr_accessor :cork
10 | 
11 |     def initialize(argv)
12 |       @refs = argv.arguments! unless argv.arguments.empty?
13 |       @cork = Cork::Board.new(silent: argv.option("silent", false),
14 |                               verbose: argv.option("verbose", false))
15 |       super
16 |     end
17 | 
18 |     self.description = <<-DESC
19 |       Converts a collection of file paths of Danger plugins into a JSON format.
20 |     DESC
21 | 
22 |     self.arguments = [
23 |       CLAide::Argument.new("Paths, Gems or Nothing", false, true)
24 |     ]
25 | 
26 |     def run
27 |       file_resolver = PluginFileResolver.new(@refs)
28 |       data = file_resolver.resolve
29 | 
30 |       parser = PluginParser.new(data[:paths])
31 |       parser.parse
32 |       json = parser.to_json
33 | 
34 |       # Append gem metadata into every plugin
35 |       data[:gems].each do |gem_data|
36 |         json.each do |plugin|
37 |           plugin[:gem_metadata] = gem_data if plugin[:gem] == gem_data[:gem]
38 |         end
39 |       end
40 | 
41 |       cork.puts json.to_json
42 |     end
43 |   end
44 | end
45 | 


--------------------------------------------------------------------------------
/lib/danger/commands/plugins/plugin_lint.rb:
--------------------------------------------------------------------------------
 1 | require "danger/plugin_support/plugin_parser"
 2 | require "danger/plugin_support/plugin_file_resolver"
 3 | require "danger/plugin_support/plugin_linter"
 4 | 
 5 | module Danger
 6 |   class PluginLint < CLAide::Command::Plugins
 7 |     self.summary = "Lints a plugin"
 8 |     self.command = "lint"
 9 | 
10 |     attr_accessor :cork
11 | 
12 |     def initialize(argv)
13 |       @warnings_as_errors = argv.flag?("warnings-as-errors", false)
14 |       @refs = argv.arguments! unless argv.arguments.empty?
15 |       @cork = Cork::Board.new(silent: argv.option("silent", false),
16 |                               verbose: argv.option("verbose", false))
17 |       super
18 |     end
19 | 
20 |     self.description = <<-DESC
21 |       Converts a collection of file paths of Danger plugins into a JSON format.
22 |       Note: Before 1.0, it will also parse the represented JSON to validate whether https://danger.systems would
23 |       show the plugin on the website.
24 |     DESC
25 | 
26 |     self.arguments = [
27 |       CLAide::Argument.new("Paths, Gems or Nothing", false, true)
28 |     ]
29 | 
30 |     def self.options
31 |       [
32 |         ["--warnings-as-errors", "Ensure strict linting."]
33 |       ].concat(super)
34 |     end
35 | 
36 |     def run
37 |       file_resolver = PluginFileResolver.new(@refs)
38 |       data = file_resolver.resolve
39 | 
40 |       parser = PluginParser.new(data[:paths], verbose: true)
41 |       parser.parse
42 |       json = parser.to_json
43 | 
44 |       linter = PluginLinter.new(json)
45 |       linter.lint
46 |       linter.print_summary(cork)
47 | 
48 |       abort("Failing due to errors\n".red) if linter.failed?
49 |       abort("Failing due to warnings as errors\n".red) if @warnings_as_errors && !linter.warnings.empty?
50 |     end
51 |   end
52 | end
53 | 


--------------------------------------------------------------------------------
/lib/danger/commands/plugins/plugin_readme.rb:
--------------------------------------------------------------------------------
 1 | require "danger/plugin_support/plugin_parser"
 2 | require "danger/plugin_support/plugin_file_resolver"
 3 | require "json"
 4 | require "erb"
 5 | 
 6 | module Danger
 7 |   class PluginReadme < CLAide::Command::Plugins
 8 |     self.summary = "Generates a README from a set of plugins"
 9 |     self.command = "readme"
10 | 
11 |     attr_accessor :cork, :json, :markdown
12 | 
13 |     def initialize(argv)
14 |       @refs = argv.arguments! unless argv.arguments.empty?
15 |       @cork = Cork::Board.new(silent: argv.option("silent", false),
16 |                               verbose: argv.option("verbose", false))
17 |       super
18 |     end
19 | 
20 |     self.description = <<-DESC
21 |       Converts a collection of file paths of Danger plugins into a format usable in a README.
22 |       This is useful for Danger itself, but also for any plugins wanting to showcase their API.
23 |     DESC
24 | 
25 |     self.arguments = [
26 |       CLAide::Argument.new("Paths, Gems or Nothing", false, true)
27 |     ]
28 | 
29 |     def run
30 |       file_resolver = PluginFileResolver.new(@refs)
31 |       data = file_resolver.resolve
32 | 
33 |       parser = PluginParser.new(data[:paths])
34 |       parser.parse
35 | 
36 |       self.json = JSON.parse(parser.to_json_string)
37 | 
38 |       template = File.join(Danger.gem_path, "lib/danger/plugin_support/templates/readme_table.html.erb")
39 |       cork.puts ERB.new(File.read(template), trim_mode: "-").result(binding)
40 |     end
41 |   end
42 | end
43 | 


--------------------------------------------------------------------------------
/lib/danger/commands/staging.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/local_helpers/pry_setup"
 2 | require "fileutils"
 3 | require "tmpdir"
 4 | 
 5 | module Danger
 6 |   class Staging < Runner
 7 |     self.summary = "Run the Dangerfile locally against local master"
 8 |     self.command = "staging"
 9 | 
10 |     def self.options
11 |       [
12 |         ["--pry", "Drop into a Pry shell after evaluating the Dangerfile."]
13 |       ]
14 |     end
15 | 
16 |     def initialize(argv)
17 |       show_help = true if argv.arguments == ["-h"]
18 | 
19 |       # Currently CLAide doesn't support short option like -h https://github.com/CocoaPods/CLAide/pull/60
20 |       # when user pass in -h, mimic the behavior of passing in --help.
21 |       argv = CLAide::ARGV.new ["--help"] if show_help
22 | 
23 |       super
24 | 
25 |       if argv.flag?("pry", false)
26 |         @dangerfile_path = PrySetup.new(cork).setup_pry(@dangerfile_path, Staging.command)
27 |       end
28 |     end
29 | 
30 |     def validate!
31 |       super
32 |       unless @dangerfile_path
33 |         help! "Could not find a Dangerfile."
34 |       end
35 |     end
36 | 
37 |     def run
38 |       ENV["DANGER_USE_LOCAL_ONLY_GIT"] = "YES"
39 | 
40 |       env = EnvironmentManager.new(ENV, cork)
41 |       dm = Dangerfile.new(env, cork)
42 | 
43 |       dm.run(
44 |         Danger::EnvironmentManager.danger_base_branch,
45 |         Danger::EnvironmentManager.danger_head_branch,
46 |         @dangerfile_path,
47 |         nil,
48 |         nil,
49 |         nil
50 |       )
51 |     end
52 |   end
53 | end
54 | 


--------------------------------------------------------------------------------
/lib/danger/commands/systems.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Systems < Runner
 3 |     self.abstract_command = true
 4 |     self.description = "For commands related to passing information from Danger to Danger.Systems."
 5 |     self.summary = "Create data for Danger.Systems."
 6 |   end
 7 | 
 8 |   class CIDocs < Systems
 9 |     self.command = "ci_docs"
10 |     self.summary = "Outputs the up-to-date CI documentation for Danger."
11 | 
12 |     def run
13 |       here = File.dirname(__FILE__)
14 |       ci_source_paths = Dir.glob(here + "/../ci_source/*.rb")
15 | 
16 |       require "yard"
17 |       # Pull out all the Danger::CI subclasses docs
18 |       registry = YARD::Registry.load(ci_source_paths, true)
19 |       ci_sources = registry.all(:class)
20 |         .select { |klass| klass.inheritance_tree.map(&:name).include? :CI }
21 |         .reject { |source| source.name == :CI }
22 |         .reject { |source| source.name == :LocalGitRepo }
23 | 
24 |       # Fail if anything is added and not documented
25 |       cis_without_docs = ci_sources.select { |source| source.base_docstring.empty? }
26 |       unless cis_without_docs.empty?
27 |         cork.puts "Please add docs to: #{cis_without_docs.map(&:name).join(', ')}"
28 |         abort("Failed.".red)
29 |       end
30 | 
31 |       # Output a JSON array of name and details
32 |       require "json"
33 |       cork.puts ci_sources.map { |ci|
34 |         {
35 |           name: ci.name,
36 |           docs: ci.docstring
37 |         }
38 |       }.to_json
39 |     end
40 |   end
41 | end
42 | 


--------------------------------------------------------------------------------
/lib/danger/comment_generators/bitbucket_server.md.erb:
--------------------------------------------------------------------------------
 1 | <%- @tables.each do |table| -%>
 2 |   <%- if table[:content].any? || table[:resolved].any? -%>
 3 | | | <%= table[:count] %> <%= table[:name] %><%= "s" unless table[:count] == 1 %> |
 4 | |---|---|
 5 |     <%- table[:content].each do |violation| -%>
 6 | | <%= @emoji_mapper.map(table[:emoji]) %> | <%= violation.message %> |
 7 |     <%- end -%>
 8 |     <%- table[:resolved].each do |message| -%>
 9 | | @emoji_mapper.map("white_check_mark") | <%= message %> |
10 |     <%- end -%>
11 | 
12 |   <%- end -%>
13 | <%- end -%>
14 | 
15 | <%- @markdowns.each do |current| -%>
16 | <%= current %>
17 | <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
18 | <%- end -%>
19 | 
20 | Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
21 | 


--------------------------------------------------------------------------------
/lib/danger/comment_generators/bitbucket_server_inline.md.erb:
--------------------------------------------------------------------------------
 1 | <%- @tables.each do |table| -%>
 2 |   <%- table[:content].each do |violation| -%>
 3 | <%= @emoji_mapper.map(table[:emoji]) if table[:emoji] %> <%= violation.message %>
 4 |   <%- end -%>
 5 |   <%- table[:resolved] && table[:resolved].each do |message| -%>
 6 | <%= @emoji_mapper.map("white_check_mark") %> <%= message %>
 7 |   <%- end -%>
 8 | <%- end -%>
 9 | 
10 | <%- @markdowns.each do |current| -%>
11 | <%= current %>
12 | <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
13 | <%- end -%>
14 | 
15 | Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
16 | 


--------------------------------------------------------------------------------
/lib/danger/comment_generators/bitbucket_server_message_group.md.erb:
--------------------------------------------------------------------------------
 1 | <%- @message_group.messages.each do |message| -%>
 2 |   <%- next if message.type == :markdown -%>
 3 | <%= @emoji_mapper.from_type(message.type) -%> <%= message.message %>
 4 | 
 5 | <%- end -%>
 6 | <%- @resolved.each do |message| -%>
 7 | <%= @emoji_mapper.map("white_check_mark") %> <%= message %>
 8 | <%- end -%>
 9 | 
10 | <%= @message_group.markdowns.map(&:message).join("\n\n") %>
11 | 
12 | Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
13 | 


--------------------------------------------------------------------------------
/lib/danger/comment_generators/github.md.erb:
--------------------------------------------------------------------------------
 1 | <!--
 2 |   <%- @tables.each do |table| -%>
 3 |   <%= pluralize(table[:name], table[:count]) %><%= ": #{truncate(table[:content].first.message)}" if table[:count] > 0 %>
 4 |   <%- end -%>
 5 |   <%= pluralize("Markdown", @markdowns.size) %>
 6 | -->
 7 | <%- @tables.each do |table| -%>
 8 |   <%- if table[:content].any? || table[:resolved].any? -%>
 9 | <table>
10 |   <thead>
11 |     <tr>
12 |       <th width="50"></th>
13 |       <th width="100%" data-danger-table="true" data-kind="<%= table[:name] %>">
14 |         <%- if table[:count] > 0 -%>
15 |           <%= pluralize(table[:name], table[:count]) %>
16 |         <%- else -%>
17 |           :white_check_mark: <%= random_compliment %>
18 |         <%- end -%>
19 |       </th>
20 |      </tr>
21 |   </thead>
22 |   <tbody>
23 |     <%- max_num_violations = FindMaxNumViolations.new(table[:content]).call -%>
24 |     <%- table[:content].take(max_num_violations).each do |violation| -%>
25 |     <tr>
26 |       <td>:<%= table[:emoji] %>:</td>
27 |       <td data-sticky="<%= violation.sticky %>"><%= violation.message %></td>
28 |     </tr>
29 |     <%- end -%>
30 |     <%- if table[:content].length > max_num_violations -%>
31 |       <tr>
32 |         <td>:warning:</td>
33 |         <td>
34 |         Danger found <%= table[:content].length %> violations with this PR. Due to GitHub's max issue comment size, the number shown has been truncated to <%= max_num_violations %>.
35 |         </td>
36 |       </tr>
37 |     <%- end -%>
38 |     <%- table[:resolved].each do |message| -%>
39 |       <tr>
40 |         <td>:white_check_mark:</td>
41 |         <td data-sticky="true"><del><%= message %></del></td>
42 |       </tr>
43 |     <%- end -%>
44 |   </tbody>
45 | </table>
46 |   <%- end -%>
47 | <%- end -%>
48 | 
49 | <%- @markdowns.each do |current| -%>
50 | <%= current %>
51 | <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
52 | <%- end -%>
53 | <p align="right" data-meta="generated_by_<%= @danger_id %>">
54 |   Generated by :no_entry_sign: <a href="https://danger.systems/">Danger</a>
55 | </p>
56 | 


--------------------------------------------------------------------------------
/lib/danger/comment_generators/github_inline.md.erb:
--------------------------------------------------------------------------------
 1 | <%- @tables.each do |table| -%>
 2 |   <%- if table[:content].any? -%>
 3 | <table data-meta="generated_by_<%= @danger_id %>">
 4 |   <tbody>
 5 |     <%- table[:content].each do |violation| -%>
 6 |     <tr>
 7 |       <td>:<%= table[:emoji] %>:</td>
 8 |       <td width="100%" data-sticky="<%= violation.sticky %>"><%= "<del>" if table[:resolved] %><%= violation.message %><%= "</del>" if table[:resolved] %></td>
 9 |     </tr>
10 |     <%- end -%>
11 |   </tbody>
12 | </table>
13 |   <%- end -%>
14 | <%- end -%>
15 | 
16 | <%- @markdowns.each do |current| -%>
17 | <%= current %>
18 | <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
19 | <%- end -%>
20 | <%# We need to add the generated_by_ to identify comments from danger. But with inlines %>
21 | <%# it might be a little annoying, so we set on the table, but if we have markdown we add the footer anyway %>
22 | <%- if @markdowns.count > 0 -%>
23 | <p align="right" data-meta="generated_by_<%= @danger_id %>">
24 |   Generated by :no_entry_sign: <a href="http://danger.systems/">Danger</a>
25 | </p>
26 | <%- end -%>
27 | 


--------------------------------------------------------------------------------
/lib/danger/comment_generators/gitlab.md.erb:
--------------------------------------------------------------------------------
 1 | <%- @tables.each do |table| -%>
 2 |   <%- if table[:content].any? || table[:resolved].any? -%>
 3 | <table>
 4 |   <thead>
 5 |     <tr>
 6 |       <th width="5%"></th>
 7 |       <th width="95%" data-danger-table="true" data-kind="<%= table[:name] %>">
 8 |         <%- if table[:count] > 0 -%>
 9 |           <%= table[:count] %> <%= table[:name] %><%= "s" unless table[:count] == 1 %>
10 |         <%- else -%>
11 |           :white_check_mark: <%= random_compliment %>
12 |         <%- end -%>
13 |       </th>
14 |      </tr>
15 |   </thead>
16 |   <tbody>
17 |     <%- table[:content].each do |violation| -%>
18 |     <tr>
19 |       <td>:<%= table[:emoji] %>:</td>
20 |       <td data-sticky="<%= violation.sticky %>"><%= violation.message %></td>
21 |     </tr>
22 |     <%- end -%>
23 |     <%- table[:resolved].each do |message| -%>
24 |       <tr>
25 |         <td>:white_check_mark:</td>
26 |         <td data-sticky="true"><del><%= message %></del></td>
27 |       </tr>
28 |     <%- end -%>
29 |   </tbody>
30 | </table>
31 |   <%- end -%>
32 | <%- end -%>
33 | 
34 | <%- @markdowns.each do |current| -%>
35 | <%= current %>
36 | <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
37 | <%- end -%>
38 | <p align="right" data-meta="generated_by_<%= @danger_id %>">
39 |   Generated by :no_entry_sign: <a href="https://github.com/danger/danger/">Danger</a>
40 | </p>
41 | 


--------------------------------------------------------------------------------
/lib/danger/comment_generators/gitlab_inline.md.erb:
--------------------------------------------------------------------------------
 1 | <%- @tables.each do |table| -%>
 2 |   <%- if table[:content].any? -%>
 3 | <table data-meta="generated_by_<%= @danger_id %>">
 4 |   <tbody>
 5 |     <%- table[:content].each do |violation| -%>
 6 |     <tr>
 7 |       <td>:<%= table[:emoji] %>:</td>
 8 |       <td width="100%" data-sticky="<%= violation.sticky %>"><%= "<del>" if table[:resolved] %><%= violation.message %><%= "</del>" if table[:resolved] %></td>
 9 |     </tr>
10 |     <%- end -%>
11 |   </tbody>
12 | </table>
13 |   <%- end -%>
14 | <%- end -%>
15 | 
16 | <%- @markdowns.each do |current| -%>
17 | <%= current %>
18 | <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
19 | <%- end -%>
20 | <%# Add the generated_by_ as a html comment to identify comments from danger. %>
21 | <!-- "generated_by_<%= @danger_id %>" -->
22 | 


--------------------------------------------------------------------------------
/lib/danger/comment_generators/vsts.md.erb:
--------------------------------------------------------------------------------
 1 | <%- @tables.each do |table| -%>
 2 |   <%- if table[:content].any? || table[:resolved].any? -%>
 3 | | | <%= table[:count] %> <%= table[:name] %><%= "s" unless table[:count] == 1 %> |
 4 | |---|---|
 5 |     <%- table[:content].each do |violation| -%>
 6 | | <%= @emoji_mapper.map(table[:emoji]) %> | <%= violation.message %> |
 7 |     <%- end -%>
 8 |     <%- table[:resolved].each do |message| -%>
 9 | | @emoji_mapper.map("white_check_mark") | <%= message %> |
10 |     <%- end -%>
11 | 
12 |   <%- end -%>
13 | <%- end -%>
14 | 
15 | <%- @markdowns.each do |current| -%>
16 | <%= current %>
17 | <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
18 | <%- end -%>
19 | 
20 | Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
21 | 


--------------------------------------------------------------------------------
/lib/danger/comment_generators/vsts_inline.md.erb:
--------------------------------------------------------------------------------
 1 | <%- @tables.each do |table| -%>
 2 |   <%- if table[:content].any? || table[:resolved].any? -%>
 3 | | | |
 4 | |---|---|
 5 |     <%- table[:content].each do |violation| -%>
 6 | | <%= @emoji_mapper.map(table[:emoji]) %> | <%= "~~" if table[:resolved] %><%= violation.message %><%= "~~" if table[:resolved] %> |
 7 |     <%- end -%>
 8 | 
 9 |   <%- end -%>
10 | <%- end -%>
11 | 
12 | <%- @markdowns.each do |current| -%>
13 | <%= current %>
14 | <%# the previous line has to be aligned far to the left, otherwise markdown can break easily %>
15 | <%- end -%>
16 | 
17 | Generated by :no_entry_sign: [Danger](https://danger.systems/ "generated_by_<%= @danger_id %>")
18 | 


--------------------------------------------------------------------------------
/lib/danger/core_ext/file_list.rb:
--------------------------------------------------------------------------------
 1 | require "danger/helpers/array_subclass"
 2 | 
 3 | module Danger
 4 |   class FileList
 5 |     include Helpers::ArraySubclass
 6 | 
 7 |     # Information about pattern: http://ruby-doc.org/core-2.2.0/File.html#method-c-fnmatch
 8 |     # e.g. "**/something.*" for any file called something with any extension
 9 |     def include?(pattern)
10 |       self.each do |current|
11 |         if !current.nil? && (File.fnmatch(pattern, current, File::FNM_EXTGLOB) || pattern == current)
12 |           return true
13 |         end
14 |       end
15 |       return false
16 |     end
17 |   end
18 | end
19 | 


--------------------------------------------------------------------------------
/lib/danger/core_ext/string.rb:
--------------------------------------------------------------------------------
 1 | class String
 2 |   # @return [String] the plural form of self determined by count
 3 |   def danger_pluralize(count)
 4 |     "#{count} #{self}#{'s' unless count == 1}"
 5 |   end
 6 | 
 7 |   # @return [String] converts to underscored, lowercase form
 8 |   def danger_underscore
 9 |     self.gsub(/::/, "/".freeze).
10 |       gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2'.freeze).
11 |       gsub(/([a-z\d])([A-Z])/, '\1_\2'.freeze).
12 |       tr("-".freeze, "_".freeze).
13 |       downcase
14 |   end
15 | 
16 |   # @return [String] truncates string with ellipsis when exceeding the limit
17 |   def danger_truncate(limit)
18 |     length > limit ? "#{self[0...limit]}..." : self
19 |   end
20 | end
21 | 


--------------------------------------------------------------------------------
/lib/danger/danger_core/dangerfile_dsl.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Dangerfile
 3 |     # Anything inside this module is considered public API, and in the future
 4 |     # documentation will be generated from it via rdoc.
 5 | 
 6 |     module DSL
 7 |       # @!group Danger Zone
 8 |       # Provides access to the raw Travis/Circle/Buildkite/GitHub objects, which
 9 |       # you can use to pull out extra bits of information. _Warning_
10 |       # the interfaces of these objects is **not** considered a part of the Dangerfile public
11 |       # API, and is viable to change occasionally on the whims of developers.
12 |       # @return [EnvironmentManager]
13 | 
14 |       attr_reader :env
15 | 
16 |       private
17 | 
18 |       def initialize
19 |         load_default_plugins
20 |       end
21 | 
22 |       def load_default_plugins
23 |         Dir["./danger_plugins/*.rb"].each do |file|
24 |           require File.expand_path(file)
25 |         end
26 |       end
27 |     end
28 |   end
29 | end
30 | 


--------------------------------------------------------------------------------
/lib/danger/danger_core/dangerfile_generator.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class DangerfileGenerator
 3 |     # returns the string for a Dangerfile based on a folder's contents'
 4 |     def self.create_dangerfile(_path, _ui)
 5 |       # Use this template for now, but this is a really ripe place to
 6 |       # improve now!
 7 |       dir = Danger.gem_path
 8 |       File.read(File.join(dir, "lib", "assets", "DangerfileTemplate"))
 9 |     end
10 |   end
11 | end
12 | 


--------------------------------------------------------------------------------
/lib/danger/danger_core/message_aggregator.rb:
--------------------------------------------------------------------------------
 1 | # frozen_string_literal: true
 2 | 
 3 | require "danger/danger_core/message_group"
 4 | require "danger/helpers/message_groups_array_helper"
 5 | 
 6 | module Danger
 7 |   class MessageAggregator
 8 |     def self.aggregate(*args, **kwargs)
 9 |       new(*args, **kwargs).aggregate
10 |     end
11 | 
12 |     def initialize(warnings: [],
13 |                    errors: [],
14 |                    messages: [],
15 |                    markdowns: [],
16 |                    danger_id: "danger")
17 |       @messages = warnings + errors + messages + markdowns
18 |       @danger_id = danger_id
19 |     end
20 | 
21 |     # aggregates the messages into an array of MessageGroups
22 |     # @return [[MessageGroup]]
23 |     def aggregate
24 |       # oookay I took some shortcuts with this one.
25 |       # first, sort messages by file and line
26 |       @messages.sort! { |a, b| a.compare_by_file_and_line(b) }
27 | 
28 |       # now create an initial empty message group
29 |       first_group = MessageGroup.new(file: nil,
30 |                                      line: nil)
31 |       @message_groups = @messages.reduce([first_group]) do |groups, msg|
32 |         # We get to take a shortcut because we sorted the messages earlier - only
33 |         # have to see if we can append msg to the last group in the list
34 |         if groups.last << msg
35 |           # we appended it, so return groups unchanged
36 |           groups
37 |         else
38 |           # have to create a new group since msg wasn't appended to the other
39 |           # group
40 |           new_group = MessageGroup.new(file: msg.file,
41 |                                        line: msg.line)
42 |           new_group << msg
43 |           groups << new_group
44 |         end
45 |       end
46 | 
47 |       @message_groups.extend(Helpers::MessageGroupsArrayHelper)
48 |     end
49 |   end
50 | end
51 | 


--------------------------------------------------------------------------------
/lib/danger/danger_core/message_group.rb:
--------------------------------------------------------------------------------
 1 | # frozen_string_literal: true
 2 | 
 3 | module Danger
 4 |   class MessageGroup
 5 |     def initialize(file: nil, line: nil)
 6 |       @file = file
 7 |       @line = line
 8 |     end
 9 | 
10 |     # Returns whether this `MessageGroup` is for the same line of code as
11 |     #   `other`, taking which file they are in to account.
12 |     # @param other [MessageGroup, Markdown, Violation]
13 |     # @return [Boolean] whether this `MessageGroup` is for the same line of code
14 |     def same_line?(other)
15 |       other.file == file && other.line == line
16 |     end
17 | 
18 |     # Merges two `MessageGroup`s that represent the same line of code
19 |     # In future, perhaps `MessageGroup` will be able to represent a group of
20 |     # messages for multiple lines.
21 |     def merge(other)
22 |       raise ArgumentError, "Cannot merge with MessageGroup for a different line" unless same_line?(other)
23 | 
24 |       @messages = (messages + other.messages).uniq
25 |     end
26 | 
27 |     # Adds a message to the group.
28 |     # @param message [Markdown, Violation] the message to add
29 |     def <<(message)
30 |       # TODO: insertion sort
31 |       return nil unless same_line?(message)
32 | 
33 |       inserted = false
34 |       messages.each.with_index do |other, idx|
35 |         if (message <=> other) == -1
36 |           inserted = true
37 |           messages.insert(idx, message)
38 |           break
39 |         end
40 |       end
41 |       messages << message unless inserted
42 |       messages
43 |     end
44 | 
45 |     # The list of messages in this group. This list will be sorted in decreasing
46 |     # order of severity (error, warning, message, markdown)
47 |     def messages
48 |       @messages ||= []
49 |     end
50 | 
51 |     attr_reader :file, :line
52 | 
53 |     # @return a hash of statistics. Currently only :warnings_count and
54 |     # :errors_count
55 |     def stats
56 |       stats = { warnings_count: 0, errors_count: 0 }
57 |       messages.each do |msg|
58 |         stats[:warnings_count] += 1 if msg.type == :warning
59 |         stats[:errors_count] += 1 if msg.type == :error
60 |       end
61 |       stats
62 |     end
63 | 
64 |     def markdowns
65 |       messages.select { |x| x.type == :markdown }
66 |     end
67 |   end
68 | end
69 | 


--------------------------------------------------------------------------------
/lib/danger/danger_core/messages/base.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class BaseMessage
 3 |     attr_accessor :message, :file, :line, :type
 4 | 
 5 |     def initialize(type:, message:, file: nil, line: nil)
 6 |       @type = type
 7 |       @message = message
 8 |       @file = file
 9 |       @line = line
10 |     end
11 | 
12 |     def compare_by_file_and_line(other)
13 |       order = cmp_nils(file, other.file)
14 |       return order unless order.nil?
15 | 
16 |       order = file <=> other.file
17 |       return order unless order.zero?
18 | 
19 |       order = cmp_nils(line, other.line)
20 |       return order unless order.nil?
21 | 
22 |       line <=> other.line
23 |     end
24 | 
25 |     # compares a and b based entirely on whether one or the other is nil
26 |     # arguments are in the same order as `a <=> b`
27 |     # nil is sorted earlier - so cmp_nils(nil, 1) => -1
28 |     #
29 |     # If neither are nil, rather than returning `a <=> b` which would seem
30 |     # like the obvious shortcut, `nil` is returned.
31 |     # This allows us to distinguish between cmp_nils returning 0 for a
32 |     # comparison of filenames, which means "a comparison on the lines is
33 |     # meaningless - you cannot have a line number for a nil file - so they
34 |     # should be sorted the same", and a <=> b returning 0, which means "the
35 |     # files are the same, so compare on the lines"
36 |     #
37 |     # @return 0, 1, -1, or nil
38 |     def cmp_nils(a, b)
39 |       if a.nil? && b.nil?
40 |         0
41 |       elsif a.nil?
42 |         -1
43 |       elsif b.nil?
44 |         1
45 |       end
46 |     end
47 | 
48 |     def eql?(other)
49 |       return self == other
50 |     end
51 | 
52 |     # @return [Boolean] returns true if is a file or line, false otherwise
53 |     def inline?
54 |       file || line
55 |     end
56 |   end
57 | end
58 | 


--------------------------------------------------------------------------------
/lib/danger/danger_core/messages/markdown.rb:
--------------------------------------------------------------------------------
 1 | # frozen_string_literal: true
 2 | 
 3 | require "danger/danger_core/messages/base"
 4 | 
 5 | module Danger
 6 |   class Markdown < BaseMessage
 7 |     def initialize(message, file = nil, line = nil)
 8 |       super(type: :markdown, message: message, file: file, line: line)
 9 |     end
10 | 
11 |     def ==(other)
12 |       return false if other.nil?
13 |       return false unless other.kind_of? self.class
14 | 
15 |       other.message == message &&
16 |         other.file == file &&
17 |         other.line == line
18 |     end
19 | 
20 |     def hash
21 |       h = 1
22 |       h = h * 31 + message.hash
23 |       h = h * 17 + file.hash
24 |       h * 17 + line.hash
25 |     end
26 | 
27 |     def to_s
28 |       extra = []
29 |       extra << "file: #{file}" unless file
30 |       extra << "line: #{line}" unless line
31 | 
32 |       "Markdown #{message} { #{extra.join ', '} }"
33 |     end
34 | 
35 |     def <=>(other)
36 |       return 1 if other.type != :markdown
37 | 
38 |       compare_by_file_and_line(other)
39 |     end
40 |   end
41 | end
42 | 


--------------------------------------------------------------------------------
/lib/danger/danger_core/messages/violation.rb:
--------------------------------------------------------------------------------
 1 | # frozen_string_literal: true
 2 | 
 3 | require "danger/danger_core/messages/base"
 4 | 
 5 | module Danger
 6 |   class Violation < BaseMessage
 7 |     VALID_TYPES = %I[error warning message].freeze
 8 |     attr_accessor :sticky
 9 | 
10 |     def initialize(message, sticky, file = nil, line = nil, type: :warning)
11 |       raise ArgumentError unless VALID_TYPES.include?(type)
12 | 
13 |       super(type: type, message: message, file: file, line: line)
14 |       self.sticky = sticky
15 |     end
16 | 
17 |     def ==(other)
18 |       return false if other.nil?
19 |       return false unless other.kind_of? self.class
20 | 
21 |       other.message == message &&
22 |         other.sticky == sticky &&
23 |         other.file == file &&
24 |         other.line == line
25 |     end
26 | 
27 |     def hash
28 |       h = 1
29 |       h = h * 31 + message.hash
30 |       h = h * 13 + sticky.hash
31 |       h = h * 17 + file.hash
32 |       h * 17 + line.hash
33 |     end
34 | 
35 |     def <=>(other)
36 |       types = VALID_TYPES + [:markdown]
37 |       order = types.index(type) <=> types.index(other.type)
38 |       return order unless order.zero?
39 | 
40 |       compare_by_file_and_line(other)
41 |     end
42 | 
43 |     def to_s
44 |       extra = []
45 |       extra << "sticky: #{sticky}"
46 |       extra << "file: #{file}" if file
47 |       extra << "line: #{line}" if line
48 |       extra << "type: #{type}"
49 | 
50 |       "Violation #{message} { #{extra.join ', '} }"
51 |     end
52 |   end
53 | end
54 | 


--------------------------------------------------------------------------------
/lib/danger/danger_core/plugins/dangerfile_local_only_plugin.rb:
--------------------------------------------------------------------------------
 1 | require "danger/plugin_support/plugin"
 2 | 
 3 | # Danger
 4 | module Danger
 5 |   # Handles interacting with local only plugin inside a Dangerfile.
 6 |   # It is support plugin for dry_run command and does not expose any methods.
 7 |   # But you can still use other plugins like git
 8 |   #
 9 |   # @example Check that added lines contains agreed form of words
10 |   #
11 |   #       git.diff.each do |chunk|
12 |   #         chunk.patch.lines.grep(/^\+/).each do |added_line|
13 |   #           if added_line.gsub!(/(?<cancel>cancel)(?<rest>[^l[[:space:]][[:punct:]]]+)/i, '>>\k<cancel>-l-\k<rest><<')
14 |   #             fail "Single 'L' for cancellation-alike words in '#{added_line}'"
15 |   #           end
16 |   #         end
17 |   #       end
18 |   #
19 |   # @see  danger/danger
20 |   # @tags core, local_only
21 |   #
22 |   class DangerfileLocalOnlyPlugin < Plugin
23 |     # So that this init can fail.
24 |     def self.new(dangerfile)
25 |       return nil if dangerfile.env.request_source.class != Danger::RequestSources::LocalOnly
26 | 
27 |       super
28 |     end
29 | 
30 |     def initialize(dangerfile)
31 |       super(dangerfile)
32 | 
33 |       @local_repo = dangerfile.env.request_source
34 |     end
35 | 
36 |     # The instance name used in the Dangerfile
37 |     # @return [String]
38 |     #
39 |     def self.instance_name
40 |       "local_repo"
41 |     end
42 |   end
43 | end
44 | 


--------------------------------------------------------------------------------
/lib/danger/helpers/array_subclass.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   module Helpers
 3 |     module ArraySubclass
 4 |       include Comparable
 5 | 
 6 |       def initialize(array)
 7 |         @__array__ = array
 8 |       end
 9 | 
10 |       def kind_of?(compare_class)
11 |         return true if compare_class == self.class
12 | 
13 |         dummy.kind_of?(compare_class)
14 |       end
15 | 
16 |       def method_missing(name, *args, &block)
17 |         super unless __array__.respond_to?(name)
18 | 
19 |         respond_to_method(name, *args, &block)
20 |       end
21 | 
22 |       def respond_to_missing?(name, include_all)
23 |         __array__.respond_to?(name, include_all) || super
24 |       end
25 | 
26 |       def to_a
27 |         __array__
28 |       end
29 | 
30 |       def to_ary
31 |         __array__
32 |       end
33 | 
34 |       def <=>(other)
35 |         return unless other.kind_of?(self.class)
36 | 
37 |         __array__ <=> other.instance_variable_get(:@__array__)
38 |       end
39 | 
40 |       private
41 | 
42 |       attr_accessor :__array__
43 | 
44 |       def dummy
45 |         Class.new(Array).new
46 |       end
47 | 
48 |       def respond_to_method(name, *args, &block)
49 |         result = __array__.send(name, *args, &block)
50 |         return result unless result.kind_of?(Array)
51 | 
52 |         if name =~ /!/
53 |           __array__ = result
54 |           self
55 |         else
56 |           self.class.new(result)
57 |         end
58 |       end
59 |     end
60 |   end
61 | end
62 | 


--------------------------------------------------------------------------------
/lib/danger/helpers/comment.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Comment
 3 |     attr_reader :id, :body
 4 | 
 5 |     def initialize(id, body, inline = nil)
 6 |       @id = id
 7 |       @body = body
 8 |       @inline = inline
 9 |     end
10 | 
11 |     def self.from_github(comment)
12 |       self.new(comment["id"], comment["body"])
13 |     end
14 | 
15 |     def self.from_gitlab(comment)
16 |       if comment.respond_to?(:id) && comment.respond_to?(:body)
17 |         type = comment.respond_to?(:type) ? comment.type : nil
18 |         self.new(comment.id, comment.body, type == "DiffNote")
19 |       else
20 |         self.new(comment["id"], comment["body"], comment["type"] == "DiffNote")
21 |       end
22 |     end
23 | 
24 |     def generated_by_danger?(danger_id)
25 |       body.include?("\"generated_by_#{danger_id}\"")
26 |     end
27 | 
28 |     def inline?
29 |       @inline.nil? ? body.include?("") : @inline
30 |     end
31 |   end
32 | end
33 | 


--------------------------------------------------------------------------------
/lib/danger/helpers/comments_parsing_helper.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   module Helpers
 3 |     module CommentsParsingHelper
 4 |       # @!group Extension points
 5 |       # Produces a message-like from a row in a comment table
 6 |       #
 7 |       # @param [String] row
 8 |       #        The content of the row in the table
 9 |       #
10 |       # @return [Violation or Markdown] the extracted message
11 |       def parse_message_from_row(row)
12 |         Violation.new(row, true)
13 |       end
14 | 
15 |       # @endgroup
16 | 
17 |       def parse_tables_from_comment(comment)
18 |         comment.split("</table>")
19 |       end
20 | 
21 |       def violations_from_table(table)
22 |         row_regex = %r{<td data-sticky="true">(?:<del>)?(.*?)(?:</del>)?\s*</td>}im
23 |         table.scan(row_regex).flatten.map do |row|
24 |           parse_message_from_row(row.strip)
25 |         end
26 |       end
27 | 
28 |       def parse_comment(comment)
29 |         tables = parse_tables_from_comment(comment)
30 |         violations = {}
31 |         tables.each do |table|
32 |           match = danger_table?(table)
33 |           next unless match
34 | 
35 |           title = match[1]
36 |           kind = table_kind_from_title(title)
37 |           next unless kind
38 | 
39 |           violations[kind] = violations_from_table(table)
40 |         end
41 | 
42 |         violations.reject { |_, v| v.empty? }
43 |       end
44 | 
45 |       def table_kind_from_title(title)
46 |         if title =~ /error/i
47 |           :error
48 |         elsif title =~ /warning/i
49 |           :warning
50 |         elsif title =~ /message/i
51 |           :message
52 |         end
53 |       end
54 | 
55 |       private
56 | 
57 |       GITHUB_OLD_REGEX = %r{<th width="100%"(.*?)</th>}im.freeze
58 |       NEW_REGEX = %r{<th.*data-danger-table="true"(.*?)</th>}im.freeze
59 | 
60 |       def danger_table?(table)
61 |         # The old GitHub specific method relied on
62 |         # the width of a `th` element to find the table
63 |         # title and determine if it was a danger table.
64 |         # The new method uses a more robust data-danger-table
65 |         # tag instead.
66 |         match = GITHUB_OLD_REGEX.match(table)
67 |         return match if match
68 | 
69 |         return NEW_REGEX.match(table)
70 |       end
71 |     end
72 |   end
73 | end
74 | 


--------------------------------------------------------------------------------
/lib/danger/helpers/emoji_mapper.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class EmojiMapper
 3 |     DATA = {
 4 |       "github" => {
 5 |         "no_entry_sign"    => "🚫",
 6 |         "warning"          => "⚠️",
 7 |         "book"             => "📖",
 8 |         "white_check_mark" => "✅"
 9 |       },
10 |       "bitbucket_server" => {
11 |         "no_entry_sign"    => ":no_entry_sign:",
12 |         "warning"          => ":warning:",
13 |         "book"             => ":blue_book:",
14 |         "white_check_mark" => ":white_check_mark:"
15 |       }
16 |     }.freeze
17 | 
18 |     TYPE_TO_EMOJI = {
19 |       error: "no_entry_sign",
20 |       warning: "warning",
21 |       message: "book"
22 |     }.freeze
23 | 
24 |     def initialize(template)
25 |       @template = DATA.has_key?(template) ? template : "github"
26 |     end
27 | 
28 |     def map(emoji)
29 |       emoji&.delete! ":"
30 |       DATA[template][emoji]
31 |     end
32 | 
33 |     def from_type(type)
34 |       map(TYPE_TO_EMOJI[type])
35 |     end
36 | 
37 |     private
38 | 
39 |     attr_reader :template
40 |   end
41 | end
42 | 


--------------------------------------------------------------------------------
/lib/danger/helpers/find_max_num_violations.rb:
--------------------------------------------------------------------------------
 1 | # Find max_num_violations in lib/danger/comment_generators/github.md.erb.
 2 | class FindMaxNumViolations
 3 |   # Save ~ 5000 for contents other than violations to avoid exceeded 65536 max comment length limit.
 4 |   LIMIT = 60_000
 5 | 
 6 |   def initialize(violations)
 7 |     @violations = violations
 8 |   end
 9 | 
10 |   def call
11 |     total = 0
12 |     num_of_violations_allowed = 0
13 | 
14 |     violations.each do |violation|
15 |       message_length = violation.message.length + 80 # 80 is ~ the size of html wraps violation message.
16 | 
17 |       if total + message_length < LIMIT
18 |         total += message_length
19 |         num_of_violations_allowed += 1
20 |       else
21 |         break
22 |       end
23 |     end
24 | 
25 |     num_of_violations_allowed
26 |   end
27 | 
28 |   private
29 | 
30 |   attr_reader :violations
31 | end
32 | 


--------------------------------------------------------------------------------
/lib/danger/helpers/message_groups_array_helper.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   module Helpers
 3 |     module MessageGroupsArrayHelper
 4 |       FakeArray = Struct.new(:count) do
 5 |         def empty?
 6 |           count.zero?
 7 |         end
 8 |       end
 9 | 
10 |       def fake_warnings_array
11 |         FakeArray.new(counts[:warnings])
12 |       end
13 | 
14 |       def fake_errors_array
15 |         FakeArray.new(counts[:errors])
16 |       end
17 | 
18 |       def counts
19 |         return @counts if @counts
20 | 
21 |         @counts = { warnings: 0, errors: 0 }
22 |         each do |message_group, counts|
23 |           group_stats = message_group.stats
24 |           @counts[:warnings] += group_stats[:warnings_count]
25 |           @counts[:errors] += group_stats[:errors_count]
26 |         end
27 |         @counts
28 |       end
29 |     end
30 |   end
31 | end
32 | 


--------------------------------------------------------------------------------
/lib/danger/plugin_support/gems_resolver.rb:
--------------------------------------------------------------------------------
 1 | require "bundler"
 2 | 
 3 | module Danger
 4 |   class GemsResolver
 5 |     def initialize(gem_names)
 6 |       @gem_names = gem_names
 7 |       @dir = Dir.mktmpdir # We want it to persist until OS cleans it on reboot
 8 |     end
 9 | 
10 |     # Returns an Array of paths (plugin lib file paths) and gems (of metadata)
11 |     def call
12 |       path_gems = []
13 | 
14 |       Bundler.with_clean_env do
15 |         Dir.chdir(dir) do
16 |           create_gemfile_from_gem_names
17 |           `bundle install --path vendor/gems`
18 |           path_gems = all_gems_metadata
19 |         end
20 |       end
21 | 
22 |       return path_gems
23 |     end
24 | 
25 |     private
26 | 
27 |     attr_reader :gem_names, :dir
28 | 
29 |     def all_gems_metadata
30 |       return paths, gems
31 |     end
32 | 
33 |     def create_gemfile_from_gem_names
34 |       gemfile = File.new("Gemfile", "w")
35 |       gemfile.write "source 'https://rubygems.org'"
36 | 
37 |       gem_names.each do |plugin|
38 |         gemfile.write "\ngem '#{plugin}'"
39 |       end
40 | 
41 |       gemfile.close
42 |     end
43 | 
44 |     # The paths are relative to dir.
45 |     def paths
46 |       relative_paths = gem_names.flat_map do |plugin|
47 |         Dir.glob("vendor/gems/ruby/*/gems/#{plugin}*/lib/**/**/**/**.rb")
48 |       end
49 | 
50 |       relative_paths.map { |path| File.join(dir, path) }
51 |     end
52 | 
53 |     def gems
54 |       real_gems.map { |gem| gem_metadata(gem) }
55 |     end
56 | 
57 |     def real_gems
58 |       spec_paths = gem_names.flat_map do |plugin|
59 |         Dir.glob("vendor/gems/ruby/*/specifications/#{plugin}*.gemspec").first
60 |       end
61 | 
62 |       spec_paths.map { |path| Gem::Specification.load path }
63 |     end
64 | 
65 |     def gem_metadata(gem)
66 |       {
67 |         name: gem.name,
68 |         gem: gem.name,
69 |         author: gem.authors,
70 |         url: gem.homepage,
71 |         description: gem.summary,
72 |         license: gem.license || "Unknown",
73 |         version: gem.version.to_s
74 |       }
75 |     end
76 |   end
77 | end
78 | 


--------------------------------------------------------------------------------
/lib/danger/plugin_support/plugin.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Plugin
 3 |     def initialize(dangerfile)
 4 |       @dangerfile = dangerfile
 5 |     end
 6 | 
 7 |     def self.instance_name
 8 |       to_s.gsub("Danger", "").danger_underscore.split("/").last
 9 |     end
10 | 
11 |     # Both of these methods exist on all objects
12 |     # http://ruby-doc.org/core-2.2.3/Kernel.html#method-i-warn
13 |     # http://ruby-doc.org/core-2.2.3/Kernel.html#method-i-fail
14 |     # However, as we're using using them in the DSL, they won't
15 |     # get method_missing called correctly.
16 |     undef :warn, :fail
17 | 
18 |     # Since we have a reference to the Dangerfile containing all the information
19 |     # We need to redirect the self calls to the Dangerfile
20 | 
21 |     def method_missing(method_sym, *arguments, **keyword_arguments, &block)
22 |       if keyword_arguments.empty?
23 |         @dangerfile.send(method_sym, *arguments, &block)
24 |       else
25 |         @dangerfile.send(method_sym, *arguments, **keyword_arguments, &block)
26 |       end
27 |     end
28 | 
29 |     def self.all_plugins
30 |       @all_plugins ||= []
31 |     end
32 | 
33 |     def self.clear_external_plugins
34 |       @all_plugins = @all_plugins.select { |plugin| Dangerfile.essential_plugin_classes.include? plugin }
35 |     end
36 | 
37 |     def self.inherited(plugin)
38 |       Plugin.all_plugins.push(plugin)
39 |     end
40 | 
41 |     private
42 | 
43 |     # When using `danger local --pry`, every plugin had an unreasonable
44 |     # amount of text output due to the Dangerfile reference in every
45 |     # plugin. So, it is filtered out. Users will start out in the context
46 |     # of the Dangerfile, and can view it by just typing `self` into the REPL.
47 |     #
48 |     def pretty_print_instance_variables
49 |       super - [:@dangerfile]
50 |     end
51 |   end
52 | end
53 | 


--------------------------------------------------------------------------------
/lib/danger/plugin_support/plugin_file_resolver.rb:
--------------------------------------------------------------------------------
 1 | require "danger/plugin_support/gems_resolver"
 2 | 
 3 | module Danger
 4 |   class PluginFileResolver
 5 |     # Takes an array of files, gems or nothing, then resolves them into
 6 |     # paths that should be sent into the documentation parser
 7 |     def initialize(references)
 8 |       @refs = references
 9 |     end
10 | 
11 |     # When given existing paths, map to absolute & existing paths
12 |     # When given a list of gems, resolve for list of gems
13 |     # When empty, imply you want to test the current lib folder as a plugin
14 |     def resolve
15 |       if !refs.nil? and refs.select { |ref| File.file? ref }.any?
16 |         paths = refs.select { |ref| File.file? ref }.map { |path| File.expand_path(path) }
17 |       elsif refs and refs.kind_of? Array
18 |         paths, gems = GemsResolver.new(refs).call
19 |       else
20 |         paths = Dir.glob(File.join(".", "lib/**/*.rb")).map { |path| File.expand_path(path) }
21 |       end
22 | 
23 |       { paths: paths, gems: gems || [] }
24 |     end
25 | 
26 |     private
27 | 
28 |     attr_reader :refs
29 |   end
30 | end
31 | 


--------------------------------------------------------------------------------
/lib/danger/plugin_support/templates/readme_table.html.erb:
--------------------------------------------------------------------------------
 1 | <% json.each do |plugin| %>
 2 | 
 3 | ### <%= plugin["instance_name"] %>
 4 | 
 5 | <%= plugin["body_md"] %>
 6 | <%- plugin["example_code"].each do |example| %>
 7 | <blockquote><%= example["title"] %>
 8 |   <pre><%= example["text"] %></pre>
 9 | </blockquote>
10 | <%- end %>
11 | 
12 | <%- unless plugin["attributes"].empty? %>
13 | #### Attributes
14 | <%- plugin["attributes"].each do |attribute| %>
15 | `<%= attribute.keys.first %>` - <%= attribute.values.first["write"]["body_md"] %>
16 | <%- end %>
17 | <%- end %>
18 | 
19 | <%- unless plugin["methods"].empty? %>
20 | #### Methods
21 | <%- plugin["methods"].each do |method| %>
22 | `<%= method["name"] %>` - <%= method["body_md"] %>
23 | <%- end %>
24 | <%- end %>
25 | 
26 | <% end %>
27 | 


--------------------------------------------------------------------------------
/lib/danger/request_sources/github/github_review_resolver.rb:
--------------------------------------------------------------------------------
 1 | require "danger/request_sources/github/github_review"
 2 | 
 3 | module Danger
 4 |   module RequestSources
 5 |     module GitHubSource
 6 |       class ReviewResolver
 7 |         def self.should_submit?(review, body)
 8 |           return !same_body?(body, review.body)
 9 |         end
10 | 
11 |         def self.same_body?(body1, body2)
12 |           return !body1.nil? && !body2.nil? && body1 == body2
13 |         end
14 |       end
15 |     end
16 |   end
17 | end
18 | 


--------------------------------------------------------------------------------
/lib/danger/request_sources/github/github_review_unsupported.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   module RequestSources
 3 |     module GitHubSource
 4 |       class ReviewUnsupported
 5 |         attr_reader :id, :body, :status, :review_json
 6 | 
 7 |         def initialize; end
 8 | 
 9 |         def start; end
10 | 
11 |         def submit; end
12 | 
13 |         def message(message, sticky = true, file = nil, line = nil); end
14 | 
15 |         def warn(message, sticky = true, file = nil, line = nil); end
16 | 
17 |         def fail(message, sticky = true, file = nil, line = nil); end
18 | 
19 |         def markdown(message, file = nil, line = nil); end
20 |       end
21 |     end
22 |   end
23 | end
24 | 


--------------------------------------------------------------------------------
/lib/danger/request_sources/local_only.rb:
--------------------------------------------------------------------------------
 1 | require "danger/helpers/comments_helper"
 2 | require "danger/helpers/comment"
 3 | 
 4 | module Danger
 5 |   module RequestSources
 6 |     class LocalOnly < RequestSource
 7 |       include Danger::Helpers::CommentsHelper
 8 |       attr_accessor :mr_json, :commits_json
 9 | 
10 |       def self.env_vars
11 |         ["DANGER_LOCAL_ONLY"]
12 |       end
13 | 
14 |       def initialize(ci_source, _environment)
15 |         self.ci_source = ci_source
16 |       end
17 | 
18 |       def validates_as_ci?
19 |         true
20 |       end
21 | 
22 |       def validates_as_api_source?
23 |         true
24 |       end
25 | 
26 |       def scm
27 |         @scm ||= GitRepo.new
28 |       end
29 | 
30 |       def setup_danger_branches
31 |         # Check that discovered values really exists
32 |         [ci_source.base_commit, ci_source.head_commit].each do |commit|
33 |           raise "Specified commit '#{commit}' not found" if scm.exec("rev-parse --quiet --verify #{commit}").empty?
34 |         end
35 | 
36 |         self.scm.exec "branch #{EnvironmentManager.danger_base_branch} #{ci_source.base_commit}"
37 |         self.scm.exec "branch #{EnvironmentManager.danger_head_branch} #{ci_source.head_commit}"
38 |       end
39 | 
40 |       def fetch_details; end
41 | 
42 |       def update_pull_request!(_hash_needed); end
43 | 
44 |       # @return [String] The organisation name, is nil if it can't be detected
45 |       def organisation
46 |         nil
47 |       end
48 |     end
49 |   end
50 | end
51 | 


--------------------------------------------------------------------------------
/lib/danger/request_sources/support/get_ignored_violation.rb:
--------------------------------------------------------------------------------
 1 | class GetIgnoredViolation
 2 |   IGNORE_REGEXP = />*\s*danger\s*:\s*ignore\s*"(?<error>[^"]*)"/i.freeze
 3 | 
 4 |   def initialize(body)
 5 |     @body = body
 6 |   end
 7 | 
 8 |   def call
 9 |     return [] unless body
10 | 
11 |     body.chomp.scan(IGNORE_REGEXP).flatten
12 |   end
13 | 
14 |   private
15 | 
16 |   attr_reader :body
17 | end
18 | 


--------------------------------------------------------------------------------
/lib/danger/version.rb:
--------------------------------------------------------------------------------
1 | module Danger
2 |   VERSION = "9.5.3".freeze
3 |   DESCRIPTION = "Like Unit Tests, but for your Team Culture.".freeze
4 | end
5 | 


--------------------------------------------------------------------------------
/spec/clients/rubygems_client_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/clients/rubygems_client"
 2 | 
 3 | RSpec.describe Danger::RubyGemsClient do
 4 |   describe ".latest_danger_version" do
 5 |     context "rubygems.org is operational" do
 6 |       it "returns latest danger version" do
 7 |         latest_version_json = IO.read("spec/fixtures/rubygems_api/api_v1_versions_danger_latest.json")
 8 |         allow(Faraday).to receive_message_chain(:get, :body) { latest_version_json }
 9 | 
10 |         result = described_class.latest_danger_version
11 | 
12 |         expect(result).to eq "3.1.1"
13 |       end
14 |     end
15 | 
16 |     context "user does not have network connection" do
17 |       it "returns dummy version" do
18 |         allow(Faraday).to receive_message_chain(:get, :body) { raise Faraday::ConnectionFailed }
19 | 
20 |         result = described_class.latest_danger_version
21 | 
22 |         expect(result).to eq described_class.const_get(:DUMMY_VERSION)
23 |       end
24 |     end
25 | 
26 |     context "rubygems.org is not operational" do
27 |       it "returns dummy version" do
28 |         allow(Faraday).to receive_message_chain(:get, :body) { raise "RubyGems.org is down 🔥" }
29 | 
30 |         result = described_class.latest_danger_version
31 | 
32 |         expect(result).to eq described_class.const_get(:DUMMY_VERSION)
33 |       end
34 |     end
35 | 
36 |     context "rubygems.org returns wrong data" do
37 |       it "returns dummy version" do
38 |         allow(Faraday).to receive_message_chain(:get, :body) { ["", nil].sample }
39 | 
40 |         result = described_class.latest_danger_version
41 | 
42 |         expect(result).to eq described_class.const_get(:DUMMY_VERSION)
43 |       end
44 |     end
45 |   end
46 | end
47 | 


--------------------------------------------------------------------------------
/spec/danger/helpers/comment_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/helpers/comment"
 2 | 
 3 | RSpec.describe Danger::Comment do
 4 |   describe ".from_github" do
 5 |     it "initializes with GitHub comment data structure" do
 6 |       github_comment = { "id" => 42, "body" => "github comment" }
 7 | 
 8 |       result = described_class.from_github(github_comment)
 9 | 
10 |       expect(result).to have_attributes(id: 42, body: "github comment")
11 |     end
12 |   end
13 | 
14 |   describe ".from_gitlab" do
15 |     it "initializes with Gitlab comment data structure" do
16 |       GitlabComment = Struct.new(:id, :body)
17 |       gitlab_comment = GitlabComment.new(42, "gitlab comment")
18 | 
19 |       result = described_class.from_gitlab(gitlab_comment)
20 | 
21 |       expect(result).to have_attributes(id: 42, body: "gitlab comment")
22 |     end
23 |   end
24 | 
25 |   describe "#generated_by_danger?" do
26 |     it "returns true when body contains generated_by_{identifier}" do
27 |       comment = described_class.new(42, '"generated_by_orta"')
28 | 
29 |       expect(comment.generated_by_danger?("orta")).to be true
30 |     end
31 | 
32 |     it "returns false when body NOT contains generated_by_{identifier}" do
33 |       comment = described_class.new(42, '"generated_by_orta"')
34 | 
35 |       expect(comment.generated_by_danger?("artsy")).to be false
36 |     end
37 | 
38 |     it "returns false when identifier is a substring of actual identifier" do
39 |       comment = described_class.new(42, '"generated_by_danger2"')
40 | 
41 |       expect(comment.generated_by_danger?("danger")).to be false
42 |     end
43 |   end
44 | end
45 | 


--------------------------------------------------------------------------------
/spec/fixtures/bitbucket_cloud_api/oauth2_response.json:
--------------------------------------------------------------------------------
1 | HTTP/1.1 200 OK
2 | 
3 | {
4 |   "access_token": "a_token",
5 |   "scopes": "pullrequest:write",
6 |   "expires_in": 7200,
7 |   "refresh_token": "a_refresh",
8 |   "token_type": "bearer"
9 | }


--------------------------------------------------------------------------------
/spec/fixtures/bitbucket_server_api/pr_response.json:
--------------------------------------------------------------------------------
1 | HTTP/1.1 200 OK
2 | 
3 | {"id":2080,"version":1,"title":"This is a danger test","description":"This PR will be used for [Danger](http://danger.systems) tests.\r\n![Danger](https://raw.githubusercontent.com/danger/design/master/images/danger_hero_shot%402x.png)","state":"DECLINED","open":false,"closed":true,"createdDate":1470864800248,"updatedDate":1470864810102,"fromRef":{"id":"refs/heads/feature/Danger","displayId":"feature/Danger","latestCommit":"c50b3f61e90dac6a00b7d0c92e415a4348bb280a","repository":{"slug":"fancyapp","id":38,"name":"fancyapp","scmId":"git","state":"AVAILABLE","statusMessage":"Available","forkable":true,"project":{"key":"IOS","id":7,"name":"ios","public":false,"type":"NORMAL","links":{"self":[{"href":"https://stash.example.com/projects/IOS"}]}},"public":false,"links":{"clone":[{"href":"https://a.user@stash.example.com/scm/ios/fancyapp.git","name":"http"},{"href":"ssh://git@stash.example.com:7999/ios/fancyapp.git","name":"ssh"}],"self":[{"href":"https://stash.example.com/projects/IOS/repos/fancyapp/browse"}]}}},"toRef":{"id":"refs/heads/develop","displayId":"develop","latestCommit":"b366c9564ad57786f0e5c6b8333c7aa1e2e90b9a","repository":{"slug":"fancyapp","id":38,"name":"fancyapp","scmId":"git","state":"AVAILABLE","statusMessage":"Available","forkable":true,"project":{"key":"IOS","id":7,"name":"ios","public":false,"type":"NORMAL","links":{"self":[{"href":"https://stash.example.com/projects/IOS"}]}},"public":false,"links":{"clone":[{"href":"https://a.user@stash.example.com/scm/ios/fancyapp.git","name":"http"},{"href":"ssh://git@stash.example.com:7999/ios/fancyapp.git","name":"ssh"}],"self":[{"href":"https://stash.example.com/projects/IOS/repos/fancyapp/browse"}]}}},"locked":false,"author":{"user":{"name":"a.user","emailAddress":"a.user@example.com","id":101,"displayName":"A User","active":true,"slug":"a.user","type":"NORMAL","links":{"self":[{"href":"https://stash.example.com/users/a.user"}]}},"role":"AUTHOR","approved":false,"status":"UNAPPROVED"},"reviewers":[],"participants":[],"links":{"self":[{"href":"https://stash.example.com/projects/IOS/repos/fancyapp/pull-requests/2080"}]}}
4 | 


--------------------------------------------------------------------------------
/spec/fixtures/ci_source/support/danger-git.log:
--------------------------------------------------------------------------------
1 | bde9ea7 Merge pull request #557 from danger/hk-spec-improvements
2 | 4a86be0 fix ruby keyword argument syntax
3 | 028fecb silence git output in specs
4 | e2ccd73 extract `with_git_repo` to spec_helper, be more explicit about git origin
5 | 3f8645a use different url to allow spec to fail
6 | 49f08b9 remove useless local variable
7 | 9c84248 Update CHANGELOG.md
8 | 0cd9198 Prepare for 3.3.0 (#556)
9 | 


--------------------------------------------------------------------------------
/spec/fixtures/ci_source/support/enterprise-remote.log:
--------------------------------------------------------------------------------
 1 | * remote origin
 2 |   Fetch URL: git@artsyhub.com:enterdanger/enterdanger.git
 3 |   Push  URL: git@artsyhub.com:enterdanger/enterdanger.git
 4 |   HEAD branch: (not queried)
 5 |   Remote branches: (status not queried)
 6 |     add-scm-provider
 7 |     bitbucket_prep
 8 |     dangerfile
 9 |     doc/fix-changelog
10 |     doc/fix-changelog-item
11 |     doc/request_source/validates_as_ci
12 |     enable-triming-whitespaces-for-all-files
13 |     feature/inline_messaging
14 |     get_local_started
15 |     git-english-env
16 |     gitlab
17 |     hk-spec-improvements
18 |     improve_warning_no_local
19 |     issues
20 |     js
21 |     line_comment
22 |     linter
23 |     master
24 |     merge_point
25 |     patch/bitbucket-server-api-inspect-protection
26 |     patch/comment
27 |     patch/danger/gem_path
28 |     patch/gemspec-homepage
29 |     patch/remove-unused-danger-class-method
30 |     patch/string-methods
31 |     patch/volation-args
32 |     readme_improvements
33 |     release
34 |     release_214
35 |     release_303
36 |     spec/orgnizations
37 |     youre_a_local_command_harry
38 |     youre_a_local_command_harry_two
39 |   Local branch configured for 'git pull':
40 |     master merges with remote master
41 |   Local ref configured for 'git push' (status not queried):
42 |     (matching) pushes to (matching)
43 | 


--------------------------------------------------------------------------------
/spec/fixtures/ci_source/support/https-remote.log:
--------------------------------------------------------------------------------
1 | * remote origin
2 |   Fetch URL: https://github.com/danger/danger.git
3 |   Push  URL: https://github.com/danger/danger.git
4 | 


--------------------------------------------------------------------------------
/spec/fixtures/ci_source/support/remote.log:
--------------------------------------------------------------------------------
 1 | * remote origin
 2 |   Fetch URL: git@github.com:danger/danger.git
 3 |   Push  URL: git@github.com:danger/danger.git
 4 |   HEAD branch: (not queried)
 5 |   Remote branches: (status not queried)
 6 |     add-scm-provider
 7 |     bitbucket_prep
 8 |     dangerfile
 9 |     doc/fix-changelog
10 |     doc/fix-changelog-item
11 |     doc/request_source/validates_as_ci
12 |     enable-triming-whitespaces-for-all-files
13 |     feature/inline_messaging
14 |     get_local_started
15 |     git-english-env
16 |     gitlab
17 |     hk-spec-improvements
18 |     improve_warning_no_local
19 |     issues
20 |     js
21 |     line_comment
22 |     linter
23 |     master
24 |     merge_point
25 |     patch/bitbucket-server-api-inspect-protection
26 |     patch/comment
27 |     patch/danger/gem_path
28 |     patch/gemspec-homepage
29 |     patch/remove-unused-danger-class-method
30 |     patch/string-methods
31 |     patch/volation-args
32 |     readme_improvements
33 |     release
34 |     release_214
35 |     release_303
36 |     spec/orgnizations
37 |     youre_a_local_command_harry
38 |     youre_a_local_command_harry_two
39 |   Local branch configured for 'git pull':
40 |     master merges with remote master
41 |   Local ref configured for 'git push' (status not queried):
42 |     (matching) pushes to (matching)
43 | 


--------------------------------------------------------------------------------
/spec/fixtures/ci_source/support/swiftweekly.github.io-git.log:
--------------------------------------------------------------------------------
 1 | 129045f Flesh out issue 38 (#89)
 2 | 300780c [38] draft update
 3 | 8f0f2d2 [38] Add accepted proposals 0139 & 0140 (#87)
 4 | 8c85645 Update 2016-09-15-issue-38.md
 5 | 65dfb06 Update Dangerfile
 6 | 684eeff Clarify publishing times in CONTRIBUTING.md (#81)
 7 | 19369ac [38] Add ClangImporter refactor (#83)
 8 | 81c2705 Update 2016-09-15-issue-38.md
 9 | 10b1ec7 Update new_draft.sh
10 | 50b40c8 Add "TODO" and Apple projects to ignored Proselint words (#78)
11 | 038d71c [38] Add Jesse's work on the SE Status Page (#85)
12 | 9fb1cf5 [38] Add extended SE-0138 (#84)
13 | 86238a7 Merge branch 'master' of https://github.com/SwiftWeekly/swiftweekly.github.io
14 | 45a666b bundle install. fix/cleanup gems
15 | acc5a1b Fix code tag (#80)
16 | c4dde6d Typo fix (#82)
17 | 3f7047a [38] Add blurb on CI cross-testing (#77)
18 | 8f8d576 [38] Add cmpcodesize starter tasks (#76)
19 | 8178b7e Merge branch 'master' of https://github.com/SwiftWeekly/swiftweekly.github.io
20 | 2606da6 [37] oops. note iOS and OSX release dates
21 | bc865bb Update CONTRIBUTING.md
22 | 78a707f Update CONTRIBUTING.md
23 | e4af163 [38] initial draft
24 | c537b81 [37] publish issue 37 (#73)
25 | e08e251 update ignored words in proselint
26 | f674bd8 [37] draft update
27 | a2776ec update ignored words
28 | ae8c9e0 update danger setup
29 | 2e4da93 setup danger + proselint
30 | 74de0a0 implement Jekyll SEO tag. close #38
31 | 7849827 [37] draft update
32 | 9a217c7 [37] draft update
33 | 793517c [37] Remove returned proposals section (#69)
34 | 704cc04 Update CONTRIBUTING.md
35 | 6dac1a5 Update CONTRIBUTING.md
36 | b5fba45 [37] Add proposal SE-0140 (#67)
37 | d1e788b [37] Add two proposals in review (#66)
38 | 7b2e552 Update CONTRIBUTING.md
39 | 2901fbd [37] initial draft
40 | ef50aea publish issue 36
41 | 3ad7efb [36] draft update
42 | f7b8ae3 [36] draft update
43 | 9bae552 [36] draft update
44 | fb4bf4d [36] Add Apple event details (#65)
45 | e89f085 [36] initial draft
46 | 84fd626 publish issue 35
47 | 0fb9734 Update CONTRIBUTING.md
48 | 71e960e Added info on Robotary (#62)
49 | 76a1671 [35] draft update
50 | 5fc8803 [35] Add -driver-time-compilation (#61)
51 | 


--------------------------------------------------------------------------------
/spec/fixtures/ci_source/support/two-kinds-of-merge-both-present.log:
--------------------------------------------------------------------------------
1 | 1234567 This is fake commit
2 | 9f8c75a Fail on errors (#2)
3 | 8e9a3ab Fixes typo in init.rb #trivial (#588)
4 | f029131 Merge pull request #2 from orta/KrauseFx-patch-1
5 | 54bff64 Initial commit
6 | 


--------------------------------------------------------------------------------
/spec/fixtures/commands/plugin_md_example.txt:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | ### proselint
 4 | 
 5 | Lint markdown files inside your projects.
 6 | This is done using the [proselint](http://proselint.com) python egg.
 7 | Results are passed out as a table in markdown.
 8 | 
 9 | <blockquote>Specifying custom CocoaPods installation options
10 |   <pre>
11 | # Runs a linter with comma style disabled
12 | proselint.disable_linters = ["misc.scare_quotes", "misc.tense_present"]
13 | proselint.lint_files "_posts/*.md"
14 | 
15 | # Runs a linter with all styles, on modified and added markpown files in this PR
16 | proselint.lint_files</pre>
17 | </blockquote>
18 | 
19 | 
20 | 
21 | #### Attributes
22 | 
23 | `disable_linters` - Allows you to disable a collection of linters from being ran.
24 | You can get a list of [them here](https://github.com/amperser/proselint#checks)
25 | 
26 | 
27 | 
28 | 
29 | #### Methods
30 | 
31 | `lint_files` - Lints the globbed files, which can fail your build if
32 | 
33 | `proselint_installed?` - Determine if proselint is currently installed in the system paths.
34 | 
35 | 
36 | 
37 | 
38 | 


--------------------------------------------------------------------------------
/spec/fixtures/comment_with_error.html:
--------------------------------------------------------------------------------
 1 | <table>
 2 |     <thead>
 3 |     <tr>
 4 |         <th width="50"></th>
 5 |         <th width="100%">1 Error</th>
 6 |     </tr>
 7 |     </thead>
 8 |     <tbody>
 9 |     <tr>
10 |         <td>:no_entry_sign:</td>
11 |         <td data-sticky="true">Some error
12 |         </td>
13 |     </tr>
14 | 
15 |     </tbody>
16 | </table>


--------------------------------------------------------------------------------
/spec/fixtures/comment_with_error_and_warnings.html:
--------------------------------------------------------------------------------
 1 | <table>
 2 |     <thead>
 3 |     <tr>
 4 |         <th width="50"></th>
 5 |         <th width="100%">1 Error</th>
 6 |     </tr>
 7 |     </thead>
 8 |     <tbody>
 9 |     <tr>
10 |         <td>:no_entry_sign:</td>
11 |         <td data-sticky="true">Some error
12 |         </td>
13 |     </tr>
14 | 
15 |     </tbody>
16 | </table>
17 | 
18 | <table>
19 |     <thead>
20 |     <tr>
21 |         <th width="50"></th>
22 |         <th width="100%">2 Warnings</th>
23 |     </tr>
24 |     </thead>
25 |     <tbody>
26 |     <tr>
27 |         <td>:warning:</td>
28 |         <td data-sticky="true">First warning
29 |         </td>
30 |         <td data-sticky="true">Second warning</td>
31 |     </tr>
32 | 
33 |     </tbody>
34 | </table>


--------------------------------------------------------------------------------
/spec/fixtures/comment_with_file_link.html:
--------------------------------------------------------------------------------
 1 | <table>
 2 |   <thead>
 3 |     <tr>
 4 |       <th width="50"></th>
 5 |       <th width="100%">1 Warning</th>
 6 |     </tr>
 7 |   </thead>
 8 |   <tbody>
 9 |   <tr>
10 |     <td>:warning:</td>
11 |     <td data-sticky="true"><a href="https://github.com/artsy/eigen/blob/13c4dc8bb61d/.gitignore#L10">.gitignore:10</a> - some warning</td>
12 |   </tr>
13 |   </tbody>
14 | </table>
15 | 


--------------------------------------------------------------------------------
/spec/fixtures/comment_with_non_sticky.html:
--------------------------------------------------------------------------------
 1 | <table>
 2 |     <thead>
 3 |     <tr>
 4 |         <th width="50"></th>
 5 |         <th width="100%">1 Error</th>
 6 |     </tr>
 7 |     </thead>
 8 |     <tbody>
 9 |     <tr>
10 |         <td>:no_entry_sign:</td>
11 |         <td data-sticky="false">Some error
12 |         </td>
13 |     </tr>
14 | 
15 |     </tbody>
16 | </table>
17 | 
18 | <table>
19 |     <thead>
20 |     <tr>
21 |         <th width="50"></th>
22 |         <th width="100%">2 Warnings</th>
23 |     </tr>
24 |     </thead>
25 |     <tbody>
26 |     <tr>
27 |         <td>:warning:</td>
28 |         <td data-sticky="true">First warning
29 |         </td>
30 |         <td>Second warning</td>
31 |     </tr>
32 | 
33 |     </tbody>
34 | </table>


--------------------------------------------------------------------------------
/spec/fixtures/comment_with_resolved_violation.html:
--------------------------------------------------------------------------------
 1 | <table>
 2 |     <thead>
 3 |     <tr>
 4 |         <th width="50"></th>
 5 |         <th width="100%" data-kind="Error">:white_check_mark: Woo!</th>
 6 |     </tr>
 7 |     </thead>
 8 |     <tbody>
 9 |     <tr>
10 |         <td>:white_check_mark:</td>
11 |         <td data-sticky="true"><del>Some error</del>
12 |         </td>
13 |     </tr>
14 | 
15 |     </tbody>
16 | </table>
17 | 
18 | <table>
19 |     <thead>
20 |     <tr>
21 |         <th width="50"></th>
22 |         <th width="100%">1 Warning</th>
23 |     </tr>
24 |     </thead>
25 |     <tbody>
26 |     <tr>
27 |         <td>:warning:</td>
28 |         <td data-sticky="true">First warning
29 |         </td>
30 |         <td data-sticky="true">Second warning</td>
31 |     </tr>
32 | 
33 |     </tbody>
34 | </table>


--------------------------------------------------------------------------------
/spec/fixtures/dangerfile_with_error:
--------------------------------------------------------------------------------
1 | # This will fail
2 | abc
3 | 


--------------------------------------------------------------------------------
/spec/fixtures/dangerfile_with_error_and_path_reassignment:
--------------------------------------------------------------------------------
1 | # This will fail
2 | path = 'asdf'
3 | abc
4 | 


--------------------------------------------------------------------------------
/spec/fixtures/github/Dangerfile:
--------------------------------------------------------------------------------
1 | # Look for prose issues
2 | prose.lint_files
3 | 
4 | # Look for spelling issues
5 | prose.ignored_words = ["Swift", "iOS", "macOS", "watchOS", "tvOS", "iPhone", "iPad", "nonnull", "nullable", "nullability", "corelibs-foundation", "corelibs-libdispatch", "stdlib", "GCD", "SwiftPM", "Xcode", "TODO", "swift-evolution", "swift-package-manager", "swift-lldb", "swift-clang", "swift-llvm", "swift-corelibs-foundation", "swift-corelibs-libdispatch", "ClangImporter"]
6 | prose.check_spelling
7 | 


--------------------------------------------------------------------------------
/spec/fixtures/github_api/pr_review_response.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "id": 15629060,
 3 |   "user": {
 4 |     "login": "conichi-ci",
 5 |     "id": 17313329,
 6 |     "avatar_url": "https://avatars3.githubusercontent.com/u/17313329?v=3",
 7 |     "gravatar_id": "",
 8 |     "url": "https://api.github.com/users/conichi-ci",
 9 |     "html_url": "https://github.com/conichi-ci",
10 |     "followers_url": "https://api.github.com/users/conichi-ci/followers",
11 |     "following_url": "https://api.github.com/users/conichi-ci/following{/other_user}",
12 |     "gists_url": "https://api.github.com/users/conichi-ci/gists{/gist_id}",
13 |     "starred_url": "https://api.github.com/users/conichi-ci/starred{/owner}{/repo}",
14 |     "subscriptions_url": "https://api.github.com/users/conichi-ci/subscriptions",
15 |     "organizations_url": "https://api.github.com/users/conichi-ci/orgs",
16 |     "repos_url": "https://api.github.com/users/conichi-ci/repos",
17 |     "events_url": "https://api.github.com/users/conichi-ci/events{/privacy}",
18 |     "received_events_url": "https://api.github.com/users/conichi-ci/received_events",
19 |     "type": "User",
20 |     "site_admin": false
21 |   },
22 |   "body": "Looks good",
23 |   "commit_id": "d076f61a9eee9806bd8c272903c4182c033e0e7e",
24 |   "state": "APPROVED",
25 |   "html_url": "https://github.com/Antondomashnev/MyAmazingDangerPlayground/pull/2#pullrequestreview-15629060",
26 |   "pull_request_url": "https://api.github.com/repos/Antondomashnev/MyAmazingDangerPlayground/pulls/2",
27 |   "_links": {
28 |     "html": {
29 |       "href": "https://github.com/Antondomashnev/MyAmazingDangerPlayground/pull/2#pullrequestreview-15629060"
30 |     },
31 |     "pull_request": {
32 |       "href": "https://api.github.com/repos/Antondomashnev/MyAmazingDangerPlayground/pulls/2"
33 |     }
34 |   },
35 |   "submitted_at": "2017-01-08T17:37:23Z"
36 | }
37 | 


--------------------------------------------------------------------------------
/spec/fixtures/gitlab_api/merge_request_1_comments_response.json:
--------------------------------------------------------------------------------
 1 | HTTP/1.1 200 OK
 2 | Server: nginx
 3 | Date: Sun, 17 Jul 2016 13:05:46 GMT
 4 | Content-Type: application/json
 5 | Content-Length: 1507
 6 | Cache-Control: max-age=0, private, must-revalidate
 7 | Etag: W/"e810a3b50890dd0a5823597abbbf9fc9"
 8 | Link: <https://gitlab.com/api/v4/projects/k0nserv/danger-test/merge_requests/593728/notes?id=k0nserv/danger-test&merge_request_id=593728&page=1&per_page=20>; rel="first", <https://gitlab.com/api/v3/projects/k0nserv/danger-test/merge_requests/593728/notes?id=k0nserv/danger-test&merge_request_id=593728&page=1&per_page=20>; rel="last"
 9 | Status: 200 OK
10 | Vary: Origin
11 | X-Next-Page:
12 | X-Page: 1
13 | X-Per-Page: 20
14 | X-Prev-Page:
15 | X-Request-Id: dee4a515-c1dd-4fec-8665-e6ce44bc6c41
16 | X-Runtime: 0.079596
17 | X-Total: 3
18 | X-Total-Pages: 1
19 | 
20 | [{"id":12717719,"body":"Added 1 commit:\n\n* 345e74fa - add b","attachment":null,"author":{"name":"Hugo Tunius","username":"k0nserv","id":483414,"state":"active","avatar_url":"https://secure.gravatar.com/avatar/7300342f996dcc9d4e22418cc9a70b14?s=80\u0026d=identicon","web_url":"https://gitlab.com/u/k0nserv"},"created_at":"2016-06-27T12:34:05.086Z","updated_at":"2016-06-27T12:34:05.086Z","system":true,"noteable_id":593728,"noteable_type":"MergeRequest","upvote?":false,"downvote?":false},{"id":12717633,"body":"Added ~392754 label","attachment":null,"author":{"name":"Hugo Tunius","username":"k0nserv","id":483414,"state":"active","avatar_url":"https://secure.gravatar.com/avatar/7300342f996dcc9d4e22418cc9a70b14?s=80\u0026d=identicon","web_url":"https://gitlab.com/u/k0nserv"},"created_at":"2016-06-27T12:30:22.157Z","updated_at":"2016-06-27T12:30:22.157Z","system":true,"noteable_id":593728,"noteable_type":"MergeRequest","upvote?":false,"downvote?":false},{"id":12716187,"body":"Added 3 commits:\n\n* dd9ed2fe...0e4db308 - 2 commits from branch `master`\n* adae7c38 - Add a","attachment":null,"author":{"name":"Hugo Tunius","username":"k0nserv","id":483414,"state":"active","avatar_url":"https://secure.gravatar.com/avatar/7300342f996dcc9d4e22418cc9a70b14?s=80\u0026d=identicon","web_url":"https://gitlab.com/u/k0nserv"},"created_at":"2016-06-27T11:26:18.621Z","updated_at":"2016-06-27T11:26:18.621Z","system":true,"noteable_id":593728,"noteable_type":"MergeRequest","upvote?":false,"downvote?":false}]
21 | 


--------------------------------------------------------------------------------
/spec/fixtures/gitlab_api/merge_request_1_discussions_empty_response.json:
--------------------------------------------------------------------------------
 1 | HTTP/1.1 200 OK
 2 | Server: nginx
 3 | Date: Thu, 14 Feb 2019 20:13:19 GMT
 4 | Content-Type: application/json
 5 | Content-Length: 7789
 6 | Cache-Control: max-age=0, private, must-revalidate
 7 | Etag: W/"5b77db1f9b56d74e705229cc87b3d5a6"
 8 | Link: <https://gitlab.com/api/v4/projects/k0nserv%2Fdanger-test/merge_requests/1/discussions?id=k0nserv%2Fdanger-test&noteable_id=1&page=1&per_page=20>; rel="first", <https://gitlab.com/api/v4/projects/k0nserv%2Fdanger-test/merge_requests/1/discussions?id=k0nserv%2Fdanger-test&noteable_id=1&page=1&per_page=20>; rel="last"
 9 | Vary: Origin
10 | X-Content-Type-Options: nosniff
11 | X-Frame-Options: SAMEORIGIN
12 | X-Next-Page: 
13 | X-Page: 1
14 | X-Per-Page: 20
15 | X-Prev-Page: 
16 | X-Request-Id: OCKNUgQKAc3
17 | X-Runtime: 0.189194
18 | X-Total: 9
19 | X-Total-Pages: 1
20 | Strict-Transport-Security: max-age=31536000
21 | RateLimit-Limit: 600
22 | RateLimit-Observed: 1
23 | RateLimit-Remaining: 599
24 | RateLimit-Reset: 1550175259
25 | RateLimit-ResetTime: Fri, 14 Feb 2019 20:14:19 GMT
26 | 
27 | []


--------------------------------------------------------------------------------
/spec/fixtures/gitlab_api/merge_request_1_response.json:
--------------------------------------------------------------------------------
 1 | HTTP/1.1 200 OK
 2 | Server: nginx
 3 | Date: Wed, 23 Jan 2019 16:54:22 GMT
 4 | Content-Type: application/json
 5 | Content-Length: 1658
 6 | Cache-Control: max-age=0, private, must-revalidate
 7 | Etag: W/"63fa6da2558d13827e3ce19a6c61f5a1"
 8 | Vary: Origin
 9 | X-Request-Id: Xq9HIN76754
10 | X-Runtime: 0.058471
11 | 
12 | {"id":593728,"iid":1,"project_id":1342007,"title":"Add a","description":"The descriptions is here\r\n\r\n\u003e Danger: ignore \"Developer specific files shouldn't be changed\"\r\n\r\n\u003e Danger: ignore \"Testing\"","state":"opened","created_at":"2016-06-27T11:04:02.114Z","updated_at":"2016-09-19T20:25:31.077Z","merged_by":null,"merged_at":null,"closed_by":null,"closed_at":null,"target_branch":"master","source_branch":"mr-test","upvotes":0,"downvotes":0,"author":{"id":483414,"name":"Hugo Tunius","username":"k0nserv","state":"active","avatar_url":"https://secure.gravatar.com/avatar/7300342f996dcc9d4e22418cc9a70b14?s=80\u0026d=identicon","web_url":"https://gitlab.com/k0nserv"},"assignee":null,"source_project_id":1342007,"target_project_id":1342007,"labels":["test-label"],"work_in_progress":false,"milestone":null,"merge_when_pipeline_succeeds":false,"merge_status":"can_be_merged","sha":"04e58de1fa97502d7e28c1394d471bb8fb1fc4a8","merge_commit_sha":null,"user_notes_count":2,"discussion_locked":null,"should_remove_source_branch":null,"force_remove_source_branch":null,"web_url":"https://gitlab.com/k0nserv/danger-test/merge_requests/1","time_stats":{"time_estimate":0,"total_time_spent":0,"human_time_estimate":null,"human_total_time_spent":null},"squash":false,"subscribed":false,"changes_count":"3","latest_build_started_at":null,"latest_build_finished_at":null,"first_deployed_to_production_at":null,"pipeline":null,"diff_refs":{"base_sha":"0e4db308b6579f7cc733e5a354e026b272e1c076","head_sha":"04e58de1fa97502d7e28c1394d471bb8fb1fc4a8","start_sha":"0e4db308b6579f7cc733e5a354e026b272e1c076"},"merge_error":null,"approvals_before_merge":null}


--------------------------------------------------------------------------------
/spec/fixtures/gitlab_api/merge_requests_response.json:
--------------------------------------------------------------------------------
 1 | HTTP/1.1 200 OK
 2 | Server: nginx
 3 | Date: Sun, 17 Jul 2016 13:05:46 GMT
 4 | Content-Type: application/json
 5 | Content-Length: 1507
 6 | Cache-Control: max-age=0, private, must-revalidate
 7 | Etag: W/"e810a3b50890dd0a5823597abbbf9fc9"
 8 | Link: <https://gitlab.com/api/v4/projects/k0nserv%2Fdanger-test/merge_requests?state=opened&page=1&per_page=20>; rel="first", <https://gitlab.com/api/v4/projects/k0nserv%2Fdanger-test/merge_requests?state=opened&page=1&per_page=20>; rel="last"
 9 | Status: 200 OK
10 | Vary: Origin
11 | X-Next-Page:
12 | X-Page: 1
13 | X-Per-Page: 20
14 | X-Prev-Page:
15 | X-Request-Id: dee4a515-c1dd-4fec-8665-e6ce44bc6c41
16 | X-Runtime: 0.079596
17 | X-Total: 3
18 | X-Total-Pages: 1
19 | 
20 | [
21 |   {
22 |     "iid": 1,
23 |     "sha": "1111111111111111111111111111111111111111"
24 |   },
25 |   {
26 |     "iid": 2,
27 |     "sha": "2222222222222222222222222222222222222222"
28 |   },
29 |   {
30 |     "iid": 3,
31 |     "sha": "3333333333333333333333333333333333333333"
32 |   }
33 | ]
34 | 


--------------------------------------------------------------------------------
/spec/fixtures/plugin_json/example_remote.json:
--------------------------------------------------------------------------------
 1 | [
 2 |   {
 3 |     "name": "ExampleRemote",
 4 |     "body_md": "",
 5 |     "instance_name": "example_remote",
 6 |     "gem": null,
 7 |     "gem_path": "",
 8 |     "files": [
 9 |       [
10 |         "/spec/fixtures/plugins/example_remote.rb",
11 |         2
12 |       ]
13 |     ],
14 |     "example_code": [
15 | 
16 |     ],
17 |     "attributes": [
18 | 
19 |     ],
20 |     "methods": [
21 |       {
22 |         "name": "echo",
23 |         "body_md": "",
24 |         "params": [
25 | 
26 |         ],
27 |         "files": [
28 |           [
29 |             "/spec/fixtures/plugins/example_remote.rb",
30 |             3
31 |           ]
32 |         ],
33 |         "tags": [
34 | 
35 |         ],
36 |         "param_couplets": {
37 |         },
38 |         "return": "",
39 |         "one_liner": "echo"
40 |       }
41 |     ],
42 |     "tags": [
43 | 
44 |     ],
45 |     "see": [
46 | 
47 |     ]
48 |   }
49 | ]


--------------------------------------------------------------------------------
/spec/fixtures/plugins/example_broken.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Dangerfile
 3 |     class ExampleBroken # not a subclass < Plugin
 4 |       def run
 5 |         return "Hi there"
 6 |       end
 7 |     end
 8 |   end
 9 | end
10 | 


--------------------------------------------------------------------------------
/spec/fixtures/plugins/example_echo_plugin.rb:
--------------------------------------------------------------------------------
1 | module Danger
2 |   class ExamplePing < Plugin
3 |     def echo
4 |       return "Hi there 🎉"
5 |     end
6 |   end
7 | end
8 | 


--------------------------------------------------------------------------------
/spec/fixtures/plugins/example_exact_path.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Dangerfile
 3 |     module DSL
 4 |       class ExampleExactPath < Plugin
 5 |         def echo
 6 |           return "Hi there exact"
 7 |         end
 8 |       end
 9 |     end
10 |   end
11 | end
12 | 


--------------------------------------------------------------------------------
/spec/fixtures/plugins/example_globbing.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class Dangerfile
 3 |     module DSL
 4 |       class ExampleGlobbing < Plugin
 5 |         def echo
 6 |           return "Hi there globbing"
 7 |         end
 8 |       end
 9 |     end
10 |   end
11 | end
12 | 


--------------------------------------------------------------------------------
/spec/fixtures/plugins/example_not_broken.rb:
--------------------------------------------------------------------------------
1 | class Dangerfile
2 |   class ExampleBroken < Danger::Plugin
3 |     def run
4 |       return "Hi there"
5 |     end
6 |   end
7 | end
8 | 


--------------------------------------------------------------------------------
/spec/fixtures/plugins/example_remote.rb:
--------------------------------------------------------------------------------
1 | module Danger
2 |   class ExampleRemote < Plugin
3 |     def echo
4 |       return "Hi there remote 🎉"
5 |     end
6 |   end
7 | end
8 | 


--------------------------------------------------------------------------------
/spec/fixtures/plugins/plugin_many_methods.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   class ExampleManyMethodsPlugin < Plugin
 3 |     def one
 4 |     end
 5 | 
 6 |     # Thing two
 7 |     #
 8 |     def two(param1)
 9 |     end
10 | 
11 |     def two_point_five(param1 = nil)
12 |     end
13 | 
14 |     # Thing three
15 |     #
16 |     # @param   [String] param1
17 |     #          A thing thing, defaults to nil.
18 |     # @return  [void]
19 |     #
20 |     def three(param1 = nil)
21 |     end
22 | 
23 |     # Thing four
24 |     #
25 |     # @param   [Number] param1
26 |     #          A thing thing, defaults to nil.
27 |     # @param   [String] param2
28 |     #          Another param
29 |     # @return  [String]
30 |     #
31 |     def four(param1 = nil, param2)
32 |     end
33 | 
34 |     # Thing five
35 |     #
36 |     # @param   [Array<String>] param1
37 |     #          A thing thing.
38 |     # @param   [Filepath] param2
39 |     #          Another param
40 |     # @return  [String]
41 |     #
42 |     def five(param1 = [], param2, param3)
43 |     end
44 | 
45 |     # Does six
46 |     # @return  [Bool]
47 |     #
48 |     def six?
49 |     end
50 | 
51 |     # Attribute docs
52 |     #
53 |     # @return   [Array<String>]
54 |     attr_accessor :seven
55 | 
56 |     attr_accessor :eight
57 |   end
58 | end
59 | 


--------------------------------------------------------------------------------
/spec/fixtures/rubygems_api/api_v1_versions_danger_latest.json:
--------------------------------------------------------------------------------
1 | {"version":"3.1.1"}
2 | 


--------------------------------------------------------------------------------
/spec/lib/danger/ci_sources/appveyor_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/ci_source/appveyor"
 2 | 
 3 | RSpec.describe Danger::AppVeyor do
 4 |   let(:valid_env) do
 5 |     {
 6 |       "APPVEYOR_PULL_REQUEST_NUMBER" => "2",
 7 |       "APPVEYOR" => "true",
 8 |       "APPVEYOR_REPO_NAME" => "artsy/eigen"
 9 |     }
10 |   end
11 | 
12 |   let(:invalid_env) do
13 |     {
14 |       "BITRISE_IO" => "true"
15 |     }
16 |   end
17 | 
18 |   let(:source) { described_class.new(valid_env) }
19 | 
20 |   describe ".validates_as_pr?" do
21 |     it "validates when the required env variables are set" do
22 |       expect(described_class.validates_as_pr?(valid_env)).to be true
23 |     end
24 | 
25 |     it "does not validate when the required env variables are not set" do
26 |       expect(described_class.validates_as_pr?(invalid_env)).to be false
27 |     end
28 | 
29 |     it "does not validate when there isn't a PR" do
30 |       valid_env["APPVEYOR_PULL_REQUEST_NUMBER"] = nil
31 |       expect(described_class.validates_as_pr?(valid_env)).to be false
32 |     end
33 |   end
34 | 
35 |   describe ".validates_as_ci?" do
36 |     it "validates when the required env variables are set" do
37 |       expect(described_class.validates_as_ci?(valid_env)).to be true
38 |     end
39 | 
40 |     it "does not validate when the required env variables are not set" do
41 |       expect(described_class.validates_as_ci?(invalid_env)).to be false
42 |     end
43 | 
44 |     it "validates even when there is no PR" do
45 |       valid_env["APPVEYOR_PULL_REQUEST_NUMBER"] = nil
46 |       expect(described_class.validates_as_ci?(valid_env)).to be true
47 |     end
48 |   end
49 | 
50 |   describe "#new" do
51 |     it "sets the repo_slug" do
52 |       expect(source.repo_slug).to eq("artsy/eigen")
53 |     end
54 | 
55 |     it "sets the pull_request_id" do
56 |       expect(source.pull_request_id).to eq("2")
57 |     end
58 | 
59 |     it "sets the repo_url", host: :github do
60 |       with_git_repo(origin: "git@github.com:artsy/eigen") do
61 |         expect(source.repo_url).to eq("git@github.com:artsy/eigen")
62 |       end
63 |     end
64 |   end
65 | end
66 | 


--------------------------------------------------------------------------------
/spec/lib/danger/ci_sources/bamboo_spec.rb:
--------------------------------------------------------------------------------
 1 | # require "danger/ci_source/bamboo"
 2 | 
 3 | RSpec.describe Danger::Bamboo do
 4 |   let(:valid_env) do
 5 |     {
 6 |         "bamboo_buildKey" => "1",
 7 |         "bamboo_repository_pr_key" => "33",
 8 |         "bamboo_planRepository_repositoryUrl" => "git@github.com:danger/danger.git"
 9 |     }
10 |   end
11 | 
12 |   let(:source) { described_class.new(valid_env) }
13 | 
14 |   describe ".validates_as_ci?" do
15 |     it "validates when the required env vars are set" do
16 |       expect(described_class.validates_as_ci?(valid_env)).to be true
17 |     end
18 | 
19 |     it "does not validate when the required env vars are not set" do
20 |       valid_env.delete "bamboo_buildKey"
21 |       expect(described_class.validates_as_ci?(valid_env)).to be false
22 |     end
23 |   end
24 | 
25 |   describe ".validates_as_pr?" do
26 |     it "validates when the required env vars are set" do
27 |       expect(described_class.validates_as_pr?(valid_env)).to be true
28 |     end
29 | 
30 |     it "does not validate when the required pull request is not set" do
31 |       valid_env["bamboo_repository_pr_key"] = nil
32 |       expect(described_class.validates_as_pr?(valid_env)).to be false
33 |     end
34 | 
35 |     it "does not validate when the required repo url is not set" do
36 |       valid_env["bamboo_planRepository_repositoryUrl"] = nil
37 |       expect(described_class.validates_as_pr?(valid_env)).to be false
38 |     end
39 |   end
40 | 
41 |   describe ".new" do
42 |     it "sets the required attributes" do
43 |       expect(source.repo_slug).to eq("danger/danger")
44 |       expect(source.pull_request_id).to eq("33")
45 |       expect(source.repo_url).to eq("git@github.com:danger/danger.git")
46 |     end
47 | 
48 |     it "supports Bitbucket Server" do
49 |       expect(source.supported_request_sources).to include(Danger::RequestSources::BitbucketServer)
50 |     end
51 |   end
52 | end
53 | 


--------------------------------------------------------------------------------
/spec/lib/danger/ci_sources/bitbucket_pipelines_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/ci_source/bitbucket_pipelines"
 2 | 
 3 | RSpec.describe Danger::BitbucketPipelines do
 4 |   let(:valid_env) do
 5 |     {
 6 |       "BITBUCKET_BUILD_NUMBER" => "2",
 7 |       "BITBUCKET_PR_ID" => "4",
 8 |       "BITBUCKET_REPO_OWNER" => "foo",
 9 |       "BITBUCKET_REPO_SLUG" => "bar"
10 |     }
11 |   end
12 | 
13 |   let(:invalid_env) do
14 |     {
15 |       "BITRISE_IO" => "true"
16 |     }
17 |   end
18 | 
19 |   let(:source) { described_class.new(valid_env) }
20 | 
21 |   describe ".validates_as_ci?" do
22 |     it "validates when the required env vars are set" do
23 |       expect(described_class.validates_as_ci?(valid_env)).to be true
24 |     end
25 | 
26 |     it "does not validate when the required env vars are not set" do
27 |       expect(described_class.validates_as_ci?(invalid_env)).to be false
28 |     end
29 |   end
30 | 
31 |   describe ".validates_as_pr?" do
32 |     it "validates when the required env vars are set" do
33 |       expect(described_class.validates_as_pr?(valid_env)).to be true
34 |     end
35 | 
36 |     it "does not validate when the required env vars are not set" do
37 |       expect(described_class.validates_as_pr?(invalid_env)).to be false
38 |     end
39 |   end
40 | 
41 |   describe ".new" do
42 |     it "sets the repository slug" do
43 |       expect(source.repo_slug).to eq("foo/bar")
44 |       expect(source.pull_request_id).to eq("4")
45 |     end
46 |   end
47 | end
48 | 


--------------------------------------------------------------------------------
/spec/lib/danger/ci_sources/buddybuild_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/ci_source/buddybuild"
 2 | 
 3 | RSpec.describe Danger::Buddybuild do
 4 |   let(:valid_env) do
 5 |     {
 6 |       "BUDDYBUILD_BUILD_ID" => "595be087b095370001d8e0b3",
 7 |       "BUDDYBUILD_PULL_REQUEST" => "4",
 8 |       "BUDDYBUILD_REPO_SLUG" => "palleas/Batman"
 9 |     }
10 |   end
11 | 
12 |   let(:source) { described_class.new(valid_env) }
13 | 
14 |   describe ".validates_as_ci?" do
15 |     it "validates when the required env vars are set" do
16 |       expect(described_class.validates_as_ci?(valid_env)).to be true
17 |     end
18 | 
19 |     it "does not validate when the required env vars are not set" do
20 |       valid_env["BUDDYBUILD_BUILD_ID"] = nil
21 |       expect(described_class.validates_as_ci?(valid_env)).to be false
22 |     end
23 |   end
24 | 
25 |   describe ".validates_as_pr?" do
26 |     it "validates when the required env vars are set" do
27 |       expect(described_class.validates_as_pr?(valid_env)).to be true
28 |     end
29 | 
30 |     it "does not validate when the required env vars are not set" do
31 |       valid_env["BUDDYBUILD_PULL_REQUEST"] = nil
32 |       expect(described_class.validates_as_pr?(valid_env)).to be false
33 |     end
34 |   end
35 | 
36 |   describe ".new" do
37 |     it "sets the repository slug" do
38 |       expect(source.repo_slug).to eq("palleas/Batman")
39 |       expect(source.pull_request_id).to eq("4")
40 |     end
41 |   end
42 | end
43 | 


--------------------------------------------------------------------------------
/spec/lib/danger/ci_sources/ci_source_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/ci_source/ci_source"
 2 | 
 3 | RSpec.describe Danger::CI do
 4 |   describe ".available_ci_sources" do
 5 |     it "returns list of CI subclasses" do
 6 |       expect(described_class.available_ci_sources.map(&:to_s)).to match_array(
 7 |         [
 8 |           "Danger::Appcenter",
 9 |           "Danger::Appcircle",
10 |           "Danger::AppVeyor",
11 |           "Danger::AzurePipelines",
12 |           "Danger::Bamboo",
13 |           "Danger::BitbucketPipelines",
14 |           "Danger::Bitrise",
15 |           "Danger::Buddybuild",
16 |           "Danger::Buildkite",
17 |           "Danger::CircleCI",
18 |           "Danger::Cirrus",
19 |           "Danger::CodeBuild",
20 |           "Danger::Codefresh",
21 |           "Danger::Codemagic",
22 |           "Danger::Codeship",
23 |           "Danger::Concourse",
24 |           "Danger::CustomCIWithGithub",
25 |           "Danger::DotCi",
26 |           "Danger::Drone",
27 |           "Danger::GitHubActions",
28 |           "Danger::GitLabCI",
29 |           "Danger::Jenkins",
30 |           "Danger::LocalGitRepo",
31 |           "Danger::LocalOnlyGitRepo",
32 |           "Danger::Screwdriver",
33 |           "Danger::Semaphore",
34 |           "Danger::Surf",
35 |           "Danger::TeamCity",
36 |           "Danger::Travis",
37 |           "Danger::XcodeCloud",
38 |           "Danger::XcodeServer"
39 |         ]
40 |       )
41 |     end
42 |   end
43 | end
44 | 


--------------------------------------------------------------------------------
/spec/lib/danger/ci_sources/local_only_git_repo_spec.rb:
--------------------------------------------------------------------------------
 1 | require "spec_helper"
 2 | require "danger/ci_source/local_only_git_repo"
 3 | 
 4 | RSpec.describe Danger::LocalOnlyGitRepo do
 5 |   def run_in_repo
 6 |     Dir.mktmpdir do |dir|
 7 |       Dir.chdir dir do
 8 |         `git init -b master`
 9 |         `git remote add origin .`
10 |         File.open(dir + "/file1", "w") {}
11 |         `git add .`
12 |         `git commit -m "adding file1"`
13 |         `git fetch`
14 |         `git checkout -b feature_branch`
15 |         File.open(dir + "/file2", "w") {}
16 |         `git add .`
17 |         `git commit -m "adding file2"`
18 | 
19 |         yield
20 |       end
21 |     end
22 |   end
23 | 
24 |   let(:valid_env) do
25 |     {
26 |       "DANGER_USE_LOCAL_ONLY_GIT" => "true"
27 |     }
28 |   end
29 | 
30 |   let(:invalid_env) do
31 |     {
32 |       "CIRCLE" => "true"
33 |     }
34 |   end
35 | 
36 |   def source(env)
37 |     described_class.new(env)
38 |   end
39 | 
40 |   describe "validates_as_ci?" do
41 |     context "when run as danger dry_run" do
42 |       it "validates as CI source" do
43 |         expect(described_class.validates_as_ci?(valid_env)).to be true
44 |       end
45 |     end
46 | 
47 |     it "does not validate as CI source outside danger dry_run" do
48 |       expect(described_class.validates_as_ci?(invalid_env)).to be false
49 |     end
50 |   end
51 | 
52 |   describe "#new" do
53 |     it "sets base_commit" do
54 |       run_in_repo do
55 |         expect(source(valid_env).base_commit).to eq("origin/master")
56 |       end
57 |     end
58 | 
59 |     it "sets head_commit" do
60 |       run_in_repo do
61 |         expect(source(valid_env).head_commit).to eq("feature_branch")
62 |       end
63 |     end
64 |   end
65 | end
66 | 


--------------------------------------------------------------------------------
/spec/lib/danger/ci_sources/screwdriver_spec.rb:
--------------------------------------------------------------------------------
 1 | # require "danger/ci_source/screwdriver"
 2 | 
 3 | RSpec.describe Danger::Screwdriver do
 4 |   let(:valid_env) do
 5 |     {
 6 |         "SCREWDRIVER" => "true",
 7 |         "SD_PULL_REQUEST" => "42",
 8 |         "SCM_URL" => "git@github.com:danger/danger.git#branch"
 9 |     }
10 |   end
11 | 
12 |   let(:source) { described_class.new(valid_env) }
13 | 
14 |   describe ".validates_as_ci?" do
15 |     it "validates when the required env vars are set" do
16 |       expect(described_class.validates_as_ci?(valid_env)).to be true
17 |     end
18 | 
19 |     it "does not validate when the required env vars are not set" do
20 |       valid_env.delete "SCREWDRIVER"
21 |       expect(described_class.validates_as_ci?(valid_env)).to be false
22 |     end
23 |   end
24 | 
25 |   describe ".validates_as_pr?" do
26 |     it "validates when the required env vars are set" do
27 |       expect(described_class.validates_as_pr?(valid_env)).to be true
28 |     end
29 | 
30 |     it "does not validate when the required pull request is not set" do
31 |       valid_env["SD_PULL_REQUEST"] = nil
32 |       expect(described_class.validates_as_pr?(valid_env)).to be false
33 |     end
34 | 
35 |     it "does not validate when the required repo url is not set" do
36 |       valid_env["SCM_URL"] = nil
37 |       expect(described_class.validates_as_pr?(valid_env)).to be false
38 |     end
39 |   end
40 | 
41 |   describe ".new" do
42 |     it "sets the required attributes" do
43 |       expect(source.repo_slug).to eq("danger/danger")
44 |       expect(source.pull_request_id).to eq("42")
45 |       expect(source.repo_url).to eq("git@github.com:danger/danger.git")
46 |     end
47 |   end
48 | end
49 | 


--------------------------------------------------------------------------------
/spec/lib/danger/ci_sources/support/find_repo_info_from_logs_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/ci_source/support/find_repo_info_from_logs"
 2 | 
 3 | RSpec.describe Danger::FindRepoInfoFromLogs do
 4 |   describe "#call" do
 5 |     it "returns repo slug from logs" do
 6 |       remote_logs = IO.read("spec/fixtures/ci_source/support/remote.log")
 7 |       finder = described_class.new("github.com", remote_logs)
 8 | 
 9 |       result = finder.call
10 | 
11 |       expect(result.slug).to eq "danger/danger"
12 |     end
13 | 
14 |     context "specify GitHub Enterprise URL" do
15 |       it "returns repo slug from logs" do
16 |         remote_logs = IO.read("spec/fixtures/ci_source/support/enterprise-remote.log")
17 |         finder = described_class.new("artsyhub.com", remote_logs)
18 | 
19 |         result = finder.call
20 | 
21 |         expect(result.slug).to eq "enterdanger/enterdanger"
22 |       end
23 |     end
24 | 
25 |     context "specify remote in https" do
26 |       it "returns repo slug from logs" do
27 |         remote_logs = IO.read("spec/fixtures/ci_source/support/https-remote.log")
28 |         finder = described_class.new("github.com", remote_logs)
29 | 
30 |         result = finder.call
31 | 
32 |         expect(result.slug).to eq "danger/danger"
33 |       end
34 |     end
35 |   end
36 | end
37 | 


--------------------------------------------------------------------------------
/spec/lib/danger/ci_sources/surf_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/ci_source/surf"
 2 | 
 3 | RSpec.describe Danger::Surf do
 4 |   let(:valid_env) do
 5 |     {
 6 |       "SURF_REPO" => "https://github.com/surf-build/surf",
 7 |       "SURF_NWO" => "surf-build/surf",
 8 |       "SURF_PR_NUM" => "29"
 9 |     }
10 |   end
11 | 
12 |   let(:invalid_env) do
13 |     {
14 |       "CIRCLE" => "true"
15 |     }
16 |   end
17 | 
18 |   let(:source) { described_class.new(valid_env) }
19 | 
20 |   describe ".validates_as_ci?" do
21 |     it "validates when the expected valid_env variables are set" do
22 |       expect(described_class.validates_as_ci?(valid_env)).to be true
23 |     end
24 | 
25 |     it "does not validated when some expected valid_env variables are missing" do
26 |       expect(described_class.validates_as_ci?(invalid_env)).to be false
27 |     end
28 |   end
29 | 
30 |   describe ".validates_as_pr?" do
31 |     it "validates when the expected valid_env variables are set" do
32 |       expect(described_class.validates_as_pr?(valid_env)).to be true
33 |     end
34 | 
35 |     it "does not validated when some expected valid_env variables are missing" do
36 |       expect(described_class.validates_as_pr?(invalid_env)).to be false
37 |     end
38 |   end
39 | 
40 |   describe "#new" do
41 |     it "sets the pull_request_id" do
42 |       expect(source.pull_request_id).to eq("29")
43 |     end
44 | 
45 |     it "sets the repo_slug" do
46 |       expect(source.repo_slug).to eq("surf-build/surf")
47 |     end
48 | 
49 |     it "sets the repo_url" do
50 |       expect(source.repo_url).to eq("https://github.com/surf-build/surf")
51 |     end
52 |   end
53 | 
54 |   describe "#supported_request_sources" do
55 |     it "supports GitHub" do
56 |       expect(source.supported_request_sources).to include(Danger::RequestSources::GitHub)
57 |     end
58 |   end
59 | end
60 | 


--------------------------------------------------------------------------------
/spec/lib/danger/commands/dry_run_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/dry_run"
 2 | require "open3"
 3 | 
 4 | RSpec.describe Danger::DryRun do
 5 |   context "prints help" do
 6 |     it "danger dry_run --help flag prints help" do
 7 |       stdout, = Open3.capture3("danger dry_run -h")
 8 |       expect(stdout).to include "Usage"
 9 |     end
10 | 
11 |     it "danger dry_run -h prints help" do
12 |       stdout, = Open3.capture3("danger dry-run -h")
13 |       expect(stdout).to include "Usage"
14 |     end
15 |   end
16 | 
17 |   describe ".options" do
18 |     it "contains extra options for local command" do
19 |       result = described_class.options
20 | 
21 |       expect(result).to include ["--pry", "Drop into a Pry shell after evaluating the Dangerfile."]
22 |     end
23 |   end
24 | end
25 | 


--------------------------------------------------------------------------------
/spec/lib/danger/commands/init_helpers/interviewer_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/init_helpers/interviewer"
 2 | 
 3 | RSpec.describe Danger::Interviewer do
 4 |   let(:cork) { double("cork") }
 5 |   let(:interviewer) { Danger::Interviewer.new(cork) }
 6 | 
 7 |   describe "#link" do
 8 |     before do
 9 |       allow(interviewer).to receive(:say)
10 |     end
11 | 
12 |     it "link URL is decorated" do
13 |       interviewer.link("http://danger.systems/")
14 |       expect(interviewer).to have_received(:say).with(" -> \e[4mhttp://danger.systems/\e[0m\n")
15 |     end
16 |   end
17 | end
18 | 


--------------------------------------------------------------------------------
/spec/lib/danger/commands/init_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/init"
 2 | 
 3 | RSpec.describe Danger::Init do
 4 |   describe "#current_repo_slug" do
 5 |     let(:command) { Danger::Init.new CLAide::ARGV.new([]) }
 6 | 
 7 |     context "with git url" do
 8 |       it "returns correct results" do
 9 |         url = "git@github.com:author/repo.git"
10 | 
11 |         allow_any_instance_of(Danger::GitRepo).to receive(:origins).and_return(url)
12 | 
13 |         expect(command.current_repo_slug).to eq "author/repo"
14 |       end
15 |     end
16 | 
17 |     context "with github pages url" do
18 |       it "returns correct results" do
19 |         url = "https://github.com/author/repo.github.io.git"
20 | 
21 |         allow_any_instance_of(Danger::GitRepo).to receive(:origins).and_return(url)
22 | 
23 |         expect(command.current_repo_slug).to eq "author/repo.github.io"
24 |       end
25 |     end
26 | 
27 |     context "with other url" do
28 |       it "returns [Your/Repo]" do
29 |         url = "http://example.com"
30 | 
31 |         allow_any_instance_of(Danger::GitRepo).to receive(:origins).and_return(url)
32 | 
33 |         expect(command.current_repo_slug).to eq "[Your/Repo]"
34 |       end
35 |     end
36 |   end
37 | end
38 | 


--------------------------------------------------------------------------------
/spec/lib/danger/commands/local_helpers/pry_setup_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/local_helpers/pry_setup"
 2 | 
 3 | RSpec.describe Danger::PrySetup do
 4 |   before { cleanup }
 5 |   after { cleanup }
 6 | 
 7 |   describe "#setup_pry" do
 8 |     it "copies the Dangerfile and appends bindings.pry" do
 9 |       Dir.mktmpdir do |dir|
10 |         dangerfile_path = "#{dir}/Dangerfile"
11 |         File.write(dangerfile_path, "")
12 | 
13 |         dangerfile_copy = described_class
14 |           .new(testing_ui)
15 |           .setup_pry(dangerfile_path, "pr")
16 | 
17 |         expect(File).to exist(dangerfile_copy)
18 |         expect(File.read(dangerfile_copy)).to include("binding.pry; File.delete(\"_Dangerfile.tmp\")")
19 |       end
20 |     end
21 | 
22 |     it "doesn't copy a nonexistent Dangerfile" do
23 |       described_class.new(testing_ui).setup_pry("", "pr")
24 | 
25 |       expect(File).not_to exist("_Dangerfile.tmp")
26 |     end
27 | 
28 |     it "warns when the pry gem is not installed" do
29 |       ui = testing_ui
30 |       expect(Kernel).to receive(:require).with("pry").and_raise(LoadError)
31 | 
32 |       expect do
33 |         described_class.new(ui).setup_pry("Dangerfile", "pr")
34 |       end.to raise_error(SystemExit)
35 |       expect(ui.err_string).to include("Pry was not found")
36 |     end
37 | 
38 |     def cleanup
39 |       File.delete "_Dangerfile.tmp" if File.exist? "_Dangerfile.tmp"
40 |     end
41 |   end
42 | end
43 | 


--------------------------------------------------------------------------------
/spec/lib/danger/commands/local_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/local"
 2 | require "open3"
 3 | 
 4 | RSpec.describe Danger::Local do
 5 |   context "prints help" do
 6 |     it "danger local --help flag prints help" do
 7 |       stdout, = Open3.capture3("danger local -h")
 8 |       expect(stdout).to include "Usage"
 9 |     end
10 | 
11 |     it "danger local -h prints help" do
12 |       stdout, = Open3.capture3("danger local -h")
13 |       expect(stdout).to include "Usage"
14 |     end
15 |   end
16 | 
17 |   describe ".options" do
18 |     it "contains extra options for local command" do
19 |       result = described_class.options
20 | 
21 |       expect(result).to include ["--use-merged-pr=[#id]", "The ID of an already merged PR inside your history to use as a reference for the local run."]
22 |       expect(result).to include ["--clear-http-cache", "Clear the local http cache before running Danger locally."]
23 |       expect(result).to include ["--pry", "Drop into a Pry shell after evaluating the Dangerfile."]
24 |     end
25 |   end
26 | 
27 |   context "default options" do
28 |     it "pr number is nil and clear_http_cache defaults to false" do
29 |       argv = CLAide::ARGV.new([])
30 | 
31 |       result = described_class.new(argv)
32 | 
33 |       expect(result.instance_variable_get(:"@pr_num")).to eq nil
34 |       expect(result.instance_variable_get(:"@clear_http_cache")).to eq false
35 |     end
36 |   end
37 | 
38 |   describe "#run" do
39 |     before do
40 |       allow(Danger::EnvironmentManager).to receive(:new)
41 | 
42 |       @dm = instance_double(Danger::Dangerfile, run: nil)
43 |       allow(Danger::Dangerfile).to receive(:new).and_return @dm
44 | 
45 |       local_setup = instance_double(Danger::LocalSetup)
46 |       allow(local_setup).to receive(:setup).and_yield
47 |       allow(Danger::LocalSetup).to receive(:new).and_return local_setup
48 |     end
49 | 
50 |     it "passes danger_id to Dangerfile and its env" do
51 |       argv = CLAide::ARGV.new(["--danger_id=DANGER_ID"])
52 |       described_class.new(argv).run
53 |       expect(Danger::EnvironmentManager).to have_received(:new)
54 |         .with(ENV, a_kind_of(Cork::Board), "DANGER_ID")
55 |       expect(@dm).to have_received(:run)
56 |         .with(anything, anything, anything, "DANGER_ID", nil, nil)
57 |     end
58 |   end
59 | end
60 | 


--------------------------------------------------------------------------------
/spec/lib/danger/commands/plugin_json_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/plugins/plugin_json"
 2 | 
 3 | RSpec.describe Danger::PluginJSON do
 4 |   after do
 5 |     Danger::Plugin.clear_external_plugins
 6 |   end
 7 | 
 8 |   it "outputs a plugins documentation as json" do
 9 |     expect do
10 |       described_class.run(["spec/fixtures/plugins/example_fully_documented.rb"])
11 |     end.to output(/DangerProselint/).to_stdout
12 |   end
13 | end
14 | 


--------------------------------------------------------------------------------
/spec/lib/danger/commands/plugins/plugin_lint_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/plugins/plugin_lint"
 2 | 
 3 | RSpec.describe Danger::PluginLint do
 4 |   after do
 5 |     Danger::Plugin.clear_external_plugins
 6 |   end
 7 | 
 8 |   it "runs the command" do
 9 |     allow(STDOUT).to receive(:puts)
10 |     described_class.run(["spec/fixtures/plugins/example_fully_documented.rb"])
11 |   end
12 | end
13 | 


--------------------------------------------------------------------------------
/spec/lib/danger/commands/plugins/plugin_readme_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/plugins/plugin_readme"
 2 | 
 3 | RSpec.describe Danger::PluginReadme do
 4 |   after do
 5 |     Danger::Plugin.clear_external_plugins
 6 |   end
 7 | 
 8 |   it "runs the command" do
 9 |     allow(STDOUT).to receive(:puts).with(fixture_txt("commands/plugin_md_example"))
10 |     described_class.run(["spec/fixtures/plugins/example_fully_documented.rb"])
11 |   end
12 | end
13 | 


--------------------------------------------------------------------------------
/spec/lib/danger/commands/staging_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/commands/staging"
 2 | require "open3"
 3 | 
 4 | RSpec.describe Danger::Staging do
 5 |   context "prints help" do
 6 |     it "danger staging --help flag prints help" do
 7 |       stdout, = Open3.capture3("danger staging -h")
 8 |       expect(stdout).to include "Usage"
 9 |     end
10 | 
11 |     it "danger staging -h prints help" do
12 |       stdout, = Open3.capture3("danger staging -h")
13 |       expect(stdout).to include "Usage"
14 |     end
15 |   end
16 | 
17 |   describe ".options" do
18 |     it "contains extra options for staging command" do
19 |       result = described_class.options
20 | 
21 |       expect(result).to include ["--pry", "Drop into a Pry shell after evaluating the Dangerfile."]
22 |     end
23 |   end
24 | end
25 | 


--------------------------------------------------------------------------------
/spec/lib/danger/core_ext/file_list_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/core_ext/file_list"
 2 | 
 3 | RSpec.describe Danger::FileList do
 4 |   describe "#include?" do
 5 |     before do
 6 |       paths = ["path1/file_name.txt", "path1/file_name1.txt", "path2/subfolder/example.json", "path1/file_name_with_[brackets].txt"]
 7 |       @filelist = Danger::FileList.new(paths)
 8 |     end
 9 | 
10 |     it "supports exact matches" do
11 |       expect(@filelist.include?("path1/file_name.txt")).to eq(true)
12 |       expect(@filelist.include?("path1/file_name_with_[brackets].txt")).to eq(true)
13 |     end
14 | 
15 |     it "supports * for wildcards" do
16 |       expect(@filelist.include?("path1/*.txt")).to eq(true)
17 |     end
18 | 
19 |     it "supports ? for single chars" do
20 |       expect(@filelist.include?("path1/file_name.???")).to eq(true)
21 |       expect(@filelist.include?("path1/file_name.?")).to eq(false)
22 |     end
23 | 
24 |     it "returns false if nothing was found" do
25 |       expect(@filelist.include?("notFound")).to eq(false)
26 |     end
27 | 
28 |     it "returns false if file path is nil" do
29 |       @filelist = Danger::FileList.new([nil])
30 |       expect(@filelist.include?("pattern")).to eq(false)
31 |     end
32 | 
33 |     it "supports {a,b} as union of multiple patterns" do
34 |       expect(@filelist.include?("{path1/file_name.txt,path3/file_name.rb}")).to eq(true)
35 |       expect(@filelist.include?("{path1/file_name.rb,path1/file_name.js}")).to eq(false)
36 |       expect(@filelist.include?("{path1/file_name.rb,path1/file_name.js,path2/*}")).to eq(true)
37 |     end
38 |   end
39 | end
40 | 


--------------------------------------------------------------------------------
/spec/lib/danger/core_ext/string_spec.rb:
--------------------------------------------------------------------------------
 1 | RSpec.describe String do
 2 |   describe "#danger_pluralize" do
 3 |     examples = [
 4 |       { count: 0, string: "0 errors" },
 5 |       { count: 1, string: "1 error" },
 6 |       { count: 2, string: "2 errors" }
 7 |     ]
 8 | 
 9 |     examples.each do |example|
10 |       it "returns '#{example[:string]}' when count = #{example[:count]}" do
11 |         expect("error".danger_pluralize(example[:count])).to eq(example[:string])
12 |       end
13 |     end
14 |   end
15 | 
16 |   describe "#danger_underscore" do
17 |     it "converts properly" do
18 |       expect("ExampleClass".danger_underscore).to eq("example_class")
19 |     end
20 |   end
21 | 
22 |   describe "#danger_truncate" do
23 |     it "truncates strings exceeding the limit" do
24 |       expect("super long string".danger_truncate(5)).to eq("super...")
25 |     end
26 | 
27 |     it "does not truncate strings that are on the limit" do
28 |       expect("12345".danger_truncate(5)).to eq("12345")
29 |     end
30 | 
31 |     it "does not truncate strings that are within the limit" do
32 |       expect("123".danger_truncate(5)).to eq("123")
33 |     end
34 |   end
35 | end
36 | 


--------------------------------------------------------------------------------
/spec/lib/danger/danger_core/danger_spec.rb:
--------------------------------------------------------------------------------
1 | RSpec.describe Danger do
2 |   it "has a version number" do
3 |     expect(Danger::VERSION).not_to be nil
4 |   end
5 | end
6 | 


--------------------------------------------------------------------------------
/spec/lib/danger/danger_core/messages/markdown_spec.rb:
--------------------------------------------------------------------------------
 1 | require_relative "./shared_examples"
 2 | require "danger/danger_core/messages/violation"
 3 | require "danger/danger_core/messages/markdown"
 4 | 
 5 | RSpec.describe Danger::Markdown do
 6 |   subject(:markdown) { described_class.new(message, file, line) }
 7 |   let(:message) { "hello world" }
 8 |   let(:file) { nil }
 9 |   let(:line) { nil }
10 | 
11 |   describe "#initialize" do
12 |     subject { described_class.new("hello world") }
13 | 
14 |     it "defaults file to nil" do
15 |       expect(subject.file).to be nil
16 |     end
17 | 
18 |     it "defaults line to nil" do
19 |       expect(subject.line).to be nil
20 |     end
21 |   end
22 | 
23 |   describe "#<=>" do
24 |     subject { markdown <=> other }
25 |     context "when other is a Violation" do
26 |       let(:other) { Danger::Violation.new("hello world", false, other_file, other_line) }
27 |       let(:other_file) { "test" }
28 |       let(:other_line) { rand(4000) }
29 |       it { is_expected.to eq(1) }
30 |     end
31 | 
32 |     context "when other is a Markdown" do
33 |       let(:other) { Danger::Markdown.new("example message", other_file, other_line) }
34 | 
35 |       it_behaves_like "compares by file and line"
36 |     end
37 |   end
38 | end
39 | 


--------------------------------------------------------------------------------
/spec/lib/danger/danger_core/messages/shared_examples.rb:
--------------------------------------------------------------------------------
 1 | # frozen_string_literal: true
 2 | 
 3 | # This helper method and the examples are used for the specs for #<=> on Markdown and Violation
 4 | 
 5 | def random_alphas(n)
 6 |   (0...n).map { ("a".."z").to_a[rand(26)] }
 7 | end
 8 | 
 9 | RSpec.shared_examples_for "compares by line" do
10 |   context "when line is nil" do
11 |     let(:line) { nil }
12 | 
13 |     context "when other_line is nil" do
14 |       let(:other_line) { nil }
15 |       it { is_expected.to eq(0) }
16 |     end
17 | 
18 |     context "when other_line is not nil" do
19 |       let(:other_line) { 1 }
20 |       it { is_expected.to eq(-1) }
21 |     end
22 |   end
23 | 
24 |   context "when line is not nil" do
25 |     let(:line) { rand(4000) }
26 |     context "when other_line is nil" do
27 |       let(:other_line) { nil }
28 |       it { is_expected.to eq(1) }
29 |     end
30 | 
31 |     context "when lines are the same" do
32 |       let(:other_line) { line }
33 |       it { is_expected.to eq 0 }
34 |     end
35 | 
36 |     context "when line < other_line" do
37 |       let(:other_line) { line + 10 }
38 |       it { is_expected.to eq(-1) }
39 |     end
40 | 
41 |     context "when line < other_line" do
42 |       let(:other_line) { line - 10 }
43 |       it { is_expected.to eq(1) }
44 |     end
45 |   end
46 | end
47 | 
48 | RSpec.shared_examples_for "compares by file and line" do
49 |   let(:other_line) { rand(4000) }
50 |   context "when file is nil" do
51 |     let(:file) { nil }
52 | 
53 |     context "when other_file is nil" do
54 |       let(:other_file) { nil }
55 |       it { is_expected.to eq(0) }
56 |     end
57 | 
58 |     context "when other_file is not nil" do
59 |       let(:other_file) { "world.txt" }
60 |       it { is_expected.to eq(-1) }
61 |     end
62 |   end
63 | 
64 |   context "when file is not nil" do
65 |     let(:file) { "hello.txt" }
66 | 
67 |     context "when other_file is nil" do
68 |       let(:other_file) { nil }
69 |       it { is_expected.to eq(1) }
70 |     end
71 | 
72 |     context "when files are the same" do
73 |       let(:other_file) { file }
74 | 
75 |       include_examples "compares by line"
76 |     end
77 | 
78 |     context "when file < other_file" do
79 |       let(:other_file) { "world.txt" }
80 |       it { is_expected.to eq(-1) }
81 |     end
82 | 
83 |     context "when file > other_file" do
84 |       let(:other_file) { "aardvark.txt" }
85 |       it { is_expected.to eq 1 }
86 |     end
87 |   end
88 | end
89 | 


--------------------------------------------------------------------------------
/spec/lib/danger/helpers/array_subclass_spec.rb:
--------------------------------------------------------------------------------
 1 | RSpec.describe Danger::Helpers::ArraySubclass do
 2 |   class List; include Danger::Helpers::ArraySubclass; end
 3 |   class OtherList; include Danger::Helpers::ArraySubclass; end
 4 | 
 5 |   it "acts as array" do
 6 |     first_list = List.new([1, 2, 3])
 7 |     second_list = List.new([4, 5, 6])
 8 |     third_list = List.new([1, 2, 3])
 9 |     fourth_list = List.new([7, 7])
10 | 
11 |     mapped_list = first_list.map { |item| item + 1 }
12 |     concated_list = first_list + second_list
13 |     mapped_mutated_list = third_list.map! { |item| item + 10 }
14 |     deleted_from_list = fourth_list.delete_at(0)
15 |     reduced_list = first_list.each_with_object({}) do |el, accum|
16 |       accum.store(el, el)
17 |     end
18 | 
19 |     expect(first_list.length).to eq(3)
20 |     expect(mapped_list).to eq(List.new([2, 3, 4]))
21 |     expect(concated_list).to eq(List.new([1, 2, 3, 4, 5, 6]))
22 |     expect(third_list).to eq(List.new([11, 12, 13]))
23 |     expect(mapped_mutated_list).to eq(List.new([11, 12, 13]))
24 |     expect(deleted_from_list).to eq(7)
25 |     expect(fourth_list).to eq(List.new([7]))
26 |     expect(reduced_list).to eq({ 1 => 1, 2 => 2, 3 => 3 })
27 |   end
28 | 
29 |   describe "equality" do
30 |     it "equals with same class same size and same values" do
31 |       first_list = List.new([1, 2, 3])
32 |       second_list = List.new([1, 2, 3])
33 |       third_list = List.new([4, 5, 6])
34 | 
35 |       expect(first_list).to eq(second_list)
36 |       expect(first_list).not_to eq(third_list)
37 |     end
38 | 
39 |     it "not equals with other classes" do
40 |       first_list = List.new([1, 2, 3])
41 |       second_list = OtherList.new([1, 2, 3])
42 |       third_list = [4, 5, 6]
43 | 
44 |       expect(first_list).not_to eq(second_list)
45 |       expect(first_list).not_to eq(third_list)
46 |     end
47 |   end
48 | 
49 |   describe "#respond_to_missing?" do
50 |     context "with missing method" do
51 |       it "returns false" do
52 |         list = List.new([])
53 |         expect(list.respond_to?(:missing_method)).to be(false)
54 |       end
55 |     end
56 | 
57 |     context "with existing method" do
58 |       it "returns true" do
59 |         list = List.new([])
60 |         expect(list.respond_to?(:to_a)).to be(true)
61 |       end
62 |     end
63 |   end
64 | end
65 | 


--------------------------------------------------------------------------------
/spec/lib/danger/helpers/message_groups_array_helper_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/danger_core/message_group"
 2 | require "danger/helpers/message_groups_array_helper"
 3 | 
 4 | RSpec.describe Danger::Helpers::MessageGroupsArrayHelper do
 5 |   subject(:array) do
 6 |     class << message_groups
 7 |       include Danger::Helpers::MessageGroupsArrayHelper
 8 |     end
 9 |     message_groups
10 |   end
11 |   let(:message_groups) { [] }
12 | 
13 |   it { is_expected.to be_a Array }
14 |   it { is_expected.to respond_to :fake_warnings_array }
15 |   it { is_expected.to respond_to :fake_errors_array }
16 | 
17 |   shared_context "with two message groups" do
18 |     let(:message_group_a) { double(Danger::MessageGroup.new) }
19 |     let(:message_group_b) { double(Danger::MessageGroup.new) }
20 |     let(:message_groups) { [message_group_a, message_group_b] }
21 |   end
22 | 
23 |   describe "#fake_warnings_array" do
24 |     subject { array.fake_warnings_array }
25 | 
26 |     context "with no message groups" do
27 |       it "returns an fake array with a count method which returns 0" do
28 |         expect(subject.count).to eq 0
29 |       end
30 |     end
31 | 
32 |     context "with two message groups" do
33 |       include_context "with two message groups"
34 | 
35 |       before do
36 |         allow(message_group_a).to receive(:stats).and_return(warnings_count: 10, errors_count: 35)
37 |         allow(message_group_b).to receive(:stats).and_return(warnings_count: 6, errors_count: 9)
38 |       end
39 | 
40 |       it "returns an fake array with a count method which returns 0" do
41 |         expect(subject.count).to eq 16
42 |       end
43 |     end
44 |   end
45 | 
46 |   describe "#fake_errors_array" do
47 |     subject { array.fake_errors_array }
48 | 
49 |     context "with no message groups" do
50 |       it "returns an fake array with a count method which returns 0" do
51 |         expect(subject.count).to eq 0
52 |       end
53 |     end
54 | 
55 |     context "with two message groups" do
56 |       include_context "with two message groups"
57 | 
58 |       before do
59 |         allow(message_group_a).to receive(:stats).and_return(warnings_count: 10, errors_count: 35)
60 |         allow(message_group_b).to receive(:stats).and_return(warnings_count: 6, errors_count: 9)
61 |       end
62 | 
63 |       it "returns an fake array with a count method which returns 44" do
64 |         expect(subject.count).to eq 44
65 |       end
66 |     end
67 |   end
68 | end
69 | 


--------------------------------------------------------------------------------
/spec/lib/danger/plugin_support/gems_resolver_spec.rb:
--------------------------------------------------------------------------------
 1 | require "lib/danger/plugin_support/gems_resolver"
 2 | 
 3 | RSpec.describe Danger::GemsResolver do
 4 |   def expected_path(dir)
 5 |     [
 6 |       "#{dir}/vendor/gems/ruby/2.3.0/gems/danger-rubocop-0.3.0/lib/danger_plugin.rb",
 7 |       "#{dir}/vendor/gems/ruby/2.3.0/gems/danger-rubocop-0.3.0/lib/version.rb"
 8 |     ]
 9 |   end
10 | 
11 |   def expected_gems
12 |     [
13 |       {
14 |         name: "danger-rubocop",
15 |         gem: "danger-rubocop",
16 |         author: ["Ash Furrow"],
17 |         url: "https://github.com/ashfurrow/danger-rubocop",
18 |         description: "A Danger plugin for running Ruby files through Rubocop.",
19 |         license: "MIT",
20 |         version: "0.3.0"
21 |       }
22 |     ]
23 |   end
24 | 
25 |   # Mimic bundle install --path vendor/gems when install danger-rubocop
26 |   def fake_bundle_install_path_vendor_gems_in(spec_root)
27 |     FileUtils.mkdir_p("vendor/gems/ruby/2.3.0/gems/danger-rubocop-0.3.0/lib")
28 |     FileUtils.touch("vendor/gems/ruby/2.3.0/gems/danger-rubocop-0.3.0/lib/version.rb")
29 |     FileUtils.touch("vendor/gems/ruby/2.3.0/gems/danger-rubocop-0.3.0/lib/danger_plugin.rb")
30 | 
31 |     FileUtils.mkdir_p("vendor/gems/ruby/2.3.0/specifications")
32 |     FileUtils.cp(
33 |       "#{spec_root}/spec/fixtures/gemspecs/danger-rubocop.gemspec",
34 |       "vendor/gems/ruby/2.3.0/specifications/danger-rubocop-0.3.0.gemspec"
35 |     )
36 |   end
37 | 
38 |   describe "#call" do
39 |     it "create gemfile, bundle, and returns" do
40 |       spec_root = Dir.pwd
41 |       gem_names = ["danger-rubocop"]
42 |       tmpdir = Dir.mktmpdir
43 | 
44 |       # We want to control the temp dir created in gems_resolver
45 |       # to compare in our test
46 |       allow(Dir).to receive(:mktmpdir) { tmpdir }
47 | 
48 |       Dir.chdir(tmpdir) do
49 |         expect(Bundler).to receive(:with_clean_env) do
50 |           fake_bundle_install_path_vendor_gems_in(spec_root)
51 |         end
52 | 
53 |         resolver = described_class.new(gem_names)
54 |         resolver.call
55 |         result = resolver.send(:all_gems_metadata)
56 | 
57 |         expect(result).to be_a Array
58 |         expect(result.first).to match_array expected_path(tmpdir)
59 |         expect(result.last).to match_array expected_gems
60 |       end
61 |     end
62 |   end
63 | end
64 | 


--------------------------------------------------------------------------------
/spec/lib/danger/plugin_support/plugin_file_resolver_spec.rb:
--------------------------------------------------------------------------------
 1 | require "lib/danger/plugin_support/plugin_file_resolver"
 2 | 
 3 | RSpec.describe Danger::PluginFileResolver do
 4 |   describe "#resolve" do
 5 |     context "Given list of gems" do
 6 |       it "resolves for gems" do
 7 |         resolver = Danger::PluginFileResolver.new(["danger", "rails"])
 8 | 
 9 |         expect(Danger::GemsResolver).to receive_message_chain(:new, :call)
10 | 
11 |         resolver.resolve
12 |       end
13 |     end
14 |   end
15 | end
16 | 


--------------------------------------------------------------------------------
/spec/lib/danger/plugin_support/plugin_linter_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/plugin_support/plugin_parser"
 2 | require "danger/plugin_support/plugin_linter"
 3 | 
 4 | def json_doc_for_path(path)
 5 |   parser = Danger::PluginParser.new path
 6 |   parser.parse
 7 |   parser.to_json
 8 | end
 9 | 
10 | RSpec.describe Danger::PluginParser do
11 |   it "creates a set of errors for fixtured plugins" do
12 |     json = json_doc_for_path("spec/fixtures/plugins/plugin_many_methods.rb")
13 |     linter = Danger::PluginLinter.new(json)
14 |     linter.lint
15 |     titles = ["Description Markdown", "Examples", "Description", "Description"]
16 |     expect(linter.errors.map(&:title)).to eq(titles)
17 |   end
18 | 
19 |   it "creates a set of warnings for fixtured plugins" do
20 |     json = json_doc_for_path("spec/fixtures/plugins/plugin_many_methods.rb")
21 |     linter = Danger::PluginLinter.new(json)
22 |     linter.lint
23 | 
24 |     titles = ["Tags", "References", "Return Type", "Return Type", "Return Type", "Unknown Param", "Return Type"]
25 |     expect(linter.warnings.map(&:title)).to eq(titles)
26 |   end
27 | 
28 |   it "fails when there are errors" do
29 |     linter = Danger::PluginLinter.new({})
30 |     expect(linter.failed?).to eq(false)
31 | 
32 |     linter.warnings = [1, 2, 3]
33 |     expect(linter.failed?).to eq(false)
34 | 
35 |     linter.errors = [1, 2, 3]
36 |     expect(linter.failed?).to eq(true)
37 |   end
38 | 
39 |   it "handles outputting a warning" do
40 |     ui = testing_ui
41 |     linter = Danger::PluginLinter.new({})
42 |     warning = Danger::PluginLinter::Rule.new(:warning, 30, "Example Title", "Example Description", nil)
43 |     warning.metadata = { name: "NameOfExample" }
44 |     warning.type = "TypeOfThing"
45 | 
46 |     linter.warnings << warning
47 | 
48 |     linter.print_summary(ui)
49 | 
50 |     expect(ui.string).to eq("\n[!] Passed\n\nWarnings\n  - Example Title - NameOfExample (TypeOfThing):\n    - Example Description\n    - @see - https://github.com/dbgrandi/danger-prose/blob/v2.0.0/lib/danger_plugin.rb#L30\n\n")
51 |   end
52 | end
53 | 


--------------------------------------------------------------------------------
/spec/lib/danger/plugin_support/plugin_spec.rb:
--------------------------------------------------------------------------------
 1 | RSpec.describe Danger::Plugin do
 2 |   it "creates an instance name based on the class name" do
 3 |     class DangerTestClassNamePlugin < Danger::Plugin; end
 4 |     expect(DangerTestClassNamePlugin.instance_name).to eq("test_class_name_plugin")
 5 |   end
 6 | 
 7 |   it "should forward unknown method calls to the dangerfile" do
 8 |     class DangerTestForwardPlugin < Danger::Plugin; end
 9 | 
10 |     class DangerFileMock
11 |       attr_accessor :pants
12 | 
13 |       def check(*args, **kargs); end
14 |     end
15 | 
16 |     plugin = DangerTestForwardPlugin.new(DangerFileMock.new)
17 |     expect do
18 |       plugin.pants
19 |     end.to_not raise_error
20 | 
21 |     expect do
22 |       plugin.check("a", "b", verbose: true)
23 |     end.to_not raise_error
24 |   end
25 | end
26 | 


--------------------------------------------------------------------------------
/spec/lib/danger/plugins/dangerfile_bitbucket_cloud_plugin_spec.rb:
--------------------------------------------------------------------------------
 1 | RSpec.describe Danger::DangerfileBitbucketCloudPlugin, host: :bitbucket_cloud do
 2 |   let(:dangerfile) { testing_dangerfile }
 3 |   let(:plugin) { described_class.new(dangerfile) }
 4 | 
 5 |   before do
 6 |     stub_pull_request
 7 |   end
 8 | 
 9 |   describe "plugin" do
10 |     before do
11 |       dangerfile.env.request_source.fetch_details
12 |     end
13 | 
14 |     describe "#pr_json" do
15 |       it "has a non empty json" do
16 |         expect(plugin.pr_json).to be_truthy
17 |       end
18 |     end
19 | 
20 |     describe "#pr_title" do
21 |       it "has a fetched title" do
22 |         expect(plugin.pr_title).to eq("This is a danger test for bitbucket cloud")
23 |       end
24 |     end
25 | 
26 |     describe "#pr_author" do
27 |       it "has a fetched author" do
28 |         expect(plugin.pr_author).to eq("AName")
29 |       end
30 |     end
31 | 
32 |     describe "#pr_link" do
33 |       it "has a fetched link to it self" do
34 |         expect(plugin.pr_link).to eq("https://api.bitbucket.org/2.0/repositories/ios/fancyapp/pullrequests/2080")
35 |       end
36 |     end
37 | 
38 |     describe "#branch_for_base" do
39 |       it "has a fetched branch for base" do
40 |         expect(plugin.branch_for_base).to eq("develop")
41 |       end
42 |     end
43 | 
44 |     describe "#branch_for_head" do
45 |       it "has a fetched branch for head" do
46 |         expect(plugin.branch_for_head).to eq("feature/test_danger")
47 |       end
48 |     end
49 | 
50 |     describe "#base_commit" do
51 |       it "has a fetched base commit" do
52 |         expect(plugin.base_commit).to eq("9c823062cf99")
53 |       end
54 |     end
55 | 
56 |     describe "#head_commit" do
57 |       it "has a fetched head commit" do
58 |         expect(plugin.head_commit).to eq("b6f5656b6ac9")
59 |       end
60 |     end
61 |   end
62 | end
63 | 


--------------------------------------------------------------------------------
/spec/lib/danger/request_sources/github/github_review_resolver_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/request_sources/github/github_review_resolver"
 2 | require "danger/request_sources/github/github_review"
 3 | 
 4 | RSpec.describe Danger::RequestSources::GitHubSource::ReviewResolver do
 5 |   let(:review) { double(Danger::RequestSources::GitHubSource::Review) }
 6 | 
 7 |   describe "should_submit?" do
 8 |     context "when submission body the same as review has" do
 9 |       before do
10 |         allow(review).to receive(:body).and_return "super body"
11 |       end
12 | 
13 |       it "returns false" do
14 |         expect(described_class.should_submit?(review, "super body")).to be false
15 |       end
16 |     end
17 | 
18 |     context "when submission body is different to review body" do
19 |       let(:submission_body) { "submission body" }
20 | 
21 |       before do
22 |         allow(review).to receive(:body).and_return "unique body"
23 |       end
24 | 
25 |       it "returns true" do
26 |         expect(described_class.should_submit?(review, "super body")).to be true
27 |       end
28 |     end
29 |   end
30 | end
31 | 


--------------------------------------------------------------------------------
/spec/lib/danger/request_sources/local_only.rb:
--------------------------------------------------------------------------------
 1 | require "erb"
 2 | 
 3 | require "danger/request_sources/local_only"
 4 | 
 5 | RSpec.describe Danger::RequestSources::LocalOnly, host: :local do
 6 |   let(:ci) { instance_double("Danger::LocalOnlyGitRepo", base_commit: "origin/master", head_commit: "feature_branch") }
 7 |   let(:subject) { described_class.new(ci, {}) }
 8 | 
 9 |   describe "validation" do
10 |     it "validates as an API source" do
11 |       expect(subject.validates_as_api_source?).to be_truthy
12 |     end
13 | 
14 |     it "validates as CI" do
15 |       expect(subject.validates_as_ci?).to be_truthy
16 |     end
17 |   end
18 | 
19 |   describe "scm" do
20 |     it "Sets up the scm" do
21 |       expect(subject.scm).to be_kind_of(Danger::GitRepo)
22 |     end
23 |   end
24 | 
25 |   describe "#setup_danger_branches" do
26 |     before do
27 |       allow(subject.scm).to receive(:exec).and_return("found_some")
28 |     end
29 | 
30 |     context "when specified head is missing" do
31 |       before { expect(subject.scm).to receive(:exec).and_return("") }
32 | 
33 |       it "raises an error" do
34 |         expect { subject.setup_danger_branches }.to raise_error("Specified commit 'origin/master' not found")
35 |       end
36 |     end
37 | 
38 |     it "sets danger_head to feature_branch" do
39 |       expect(subject.scm).to receive(:exec).with(/^branch.*head.*feature_branch/).and_return("feature_branch")
40 |       subject.setup_danger_branches
41 |     end
42 | 
43 |     it "sets danger_base to origin/master" do
44 |       expect(subject.scm).to receive(:exec).with(%r{^branch.*base.*origin/master}).and_return("origin/master")
45 |       subject.setup_danger_branches
46 |     end
47 |   end
48 | end
49 | 


--------------------------------------------------------------------------------
/spec/lib/danger/request_sources/support/get_ignored_violation_spec.rb:
--------------------------------------------------------------------------------
 1 | RSpec.describe GetIgnoredViolation do
 2 |   describe "#call" do
 3 |     context "Without specific ignore sentence" do
 4 |       it "returns empty array" do
 5 |         result = described_class.new("No danger ignore").call
 6 | 
 7 |         expect(result).to eq []
 8 |       end
 9 |     end
10 | 
11 |     context "With specific ignore sentence" do
12 |       it "returns content in the quotes" do
13 |         sentence = %(Danger: Ignore "This build didn't pass tests")
14 |         result = described_class.new(sentence).call
15 | 
16 |         expect(result).to eq ["This build didn't pass tests"]
17 |       end
18 |     end
19 | 
20 |     context "With specific ignore sentence contains escapted quote" do
21 |       it "returns content in the quotes" do
22 |         sentence = %("ignoring this:\r\n>Danger: Ignore \"`TophatModels.v20` changed.\"")
23 |         result = described_class.new(sentence).call
24 | 
25 |         expect(result).to eq [%(`TophatModels.v20` changed.)]
26 |       end
27 |     end
28 |   end
29 | end
30 | 


--------------------------------------------------------------------------------
/spec/lib/danger/request_sources/vsts_api_spec.rb:
--------------------------------------------------------------------------------
 1 | require "danger/request_sources/vsts_api"
 2 | 
 3 | RSpec.describe Danger::RequestSources::VSTSAPI, host: :vsts do
 4 |   describe "#inspect" do
 5 |     it "masks personal access token on inspect" do
 6 |       allow(ENV).to receive(:[]).with("ENVDANGER_VSTS_API_TOKEN") { "supertopsecret" }
 7 |       api = described_class.new("danger", 1, stub_env)
 8 | 
 9 |       inspected = api.inspect
10 | 
11 |       expect(inspected).to include(%(@token="********"))
12 |     end
13 | 
14 |     it "handles http hosts" do
15 |       env = stub_env
16 |       env["DANGER_VSTS_HOST"] = "http://my_url"
17 |       api = described_class.new("danger", 1, env)
18 |       expect(api.pr_api_endpoint).to eq("http://my_url/_apis/git/repositories/danger/pullRequests/1")
19 |       env["DANGER_VSTS_HOST"] = "my_url"
20 |       api = described_class.new("danger", 1, env)
21 |       expect(api.pr_api_endpoint).to eq("https://my_url/_apis/git/repositories/danger/pullRequests/1")
22 |     end
23 | 
24 |     it "checks uses ssl only for https urls" do
25 |       env = stub_env
26 |       env["DANGER_VSTS_HOST"] = "http://my_url"
27 |       api = described_class.new("danger", 1, env)
28 |       expect(api.send(:use_ssl)).to eq(false)
29 | 
30 |       env["DANGER_VSTS_HOST"] = "https://my_url"
31 |       api = described_class.new("danger", 1, env)
32 |       expect(api.send(:use_ssl)).to eq(true)
33 |     end
34 |   end
35 | end
36 | 


--------------------------------------------------------------------------------
/spec/support/bitbucket_cloud_helper.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   module Support
 3 |     module BitbucketCloudHelper
 4 |       def stub_env
 5 |         {
 6 |           "DANGER_BITBUCKETCLOUD_USERNAME" => "a.name",
 7 |           "DANGER_BITBUCKETCLOUD_UUID" => "c91be865-efc6-49a6-93c5-24e1267c479b",
 8 |           "DANGER_BITBUCKETCLOUD_PASSWORD" => "a_password",
 9 |           "JENKINS_URL" => "http://jenkins.example.com/job/ios-check-pullrequest/",
10 |           "GIT_URL" => "ssh://git@stash.example.com:7999/ios/fancyapp.git",
11 |           "ghprbPullId" => "2080"
12 |         }
13 |       end
14 | 
15 |       def stub_ci
16 |         Danger::Jenkins.new(stub_env)
17 |       end
18 | 
19 |       def stub_request_source(env = stub_env)
20 |         Danger::RequestSources::BitbucketCloud.new(stub_ci, env)
21 |       end
22 | 
23 |       def stub_pull_request
24 |         raw_file = File.new("spec/fixtures/bitbucket_cloud_api/pr_response.json")
25 |         url = "https://api.bitbucket.org/2.0/repositories/ios/fancyapp/pullrequests/2080"
26 |         WebMock.stub_request(:get, url).to_return(raw_file)
27 |       end
28 | 
29 |       def stub_pull_requests
30 |         raw_file = File.new("spec/fixtures/bitbucket_cloud_api/prs_response.json")
31 |         url = "https://api.bitbucket.org/2.0/repositories/ios/fancyapp/pullrequests?q=source.branch.name=%22feature_branch%22"
32 |         WebMock.stub_request(:get, url).to_return(raw_file)
33 |       end
34 | 
35 |       def stub_access_token
36 |         raw_file = File.new("spec/fixtures/bitbucket_cloud_api/oauth2_response.json")
37 |         url = "https://bitbucket.org/site/oauth2/access_token"
38 |         WebMock.stub_request(:post, url).to_return(raw_file)
39 |       end
40 | 
41 |       def stub_pull_request_comment
42 |         raw_file = File.new("spec/fixtures/bitbucket_cloud_api/pr_comments.json")
43 |         url = "https://api.bitbucket.org/2.0/repositories/ios/fancyapp/pullrequests/2080/comments?pagelen=100&q=deleted+%7E+false+AND+user.uuid+%7E+%22c91be865-efc6-49a6-93c5-24e1267c479b%22"
44 |         WebMock.stub_request(:get, url).to_return(raw_file)
45 |       end
46 |     end
47 |   end
48 | end
49 | 


--------------------------------------------------------------------------------
/spec/support/bitbucket_server_helper.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   module Support
 3 |     module BitbucketServerHelper
 4 |       def stub_env
 5 |         {
 6 |           "DANGER_BITBUCKETSERVER_HOST" => "stash.example.com",
 7 |           "DANGER_BITBUCKETSERVER_USERNAME" => "a.name",
 8 |           "DANGER_BITBUCKETSERVER_PASSWORD" => "a_password",
 9 |           "JENKINS_URL" => "http://jenkins.example.com/job/ios-check-pullrequest/",
10 |           "GIT_URL" => "ssh://git@stash.example.com:7999/ios/fancyapp.git",
11 |           "ghprbPullId" => "2080"
12 |         }
13 |       end
14 | 
15 |       def stub_ci
16 |         Danger::Jenkins.new(stub_env)
17 |       end
18 | 
19 |       def stub_request_source
20 |         Danger::RequestSources::GitLab.new(stub_ci, stub_env)
21 |       end
22 | 
23 |       def stub_pull_request
24 |         raw_file = File.new("spec/fixtures/bitbucket_server_api/pr_response.json")
25 |         url = "https://stash.example.com/rest/api/1.0/projects/ios/repos/fancyapp/pull-requests/2080"
26 |         WebMock.stub_request(:get, url).to_return(raw_file)
27 |       end
28 | 
29 |       def stub_pull_request_diff
30 |         raw_file = File.new("spec/fixtures/bitbucket_server_api/pr_diff_response.json")
31 |         url = "https://stash.example.com/rest/api/1.0/projects/ios/repos/fancyapp/pull-requests/2080/diff?withComments=false"
32 |         WebMock.stub_request(:get, url).to_return(raw_file)
33 |       end
34 |     end
35 |   end
36 | end
37 | 


--------------------------------------------------------------------------------
/spec/support/github_helper.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   module Support
 3 |     module GitHubHelper
 4 |       def expected_headers
 5 |         {}
 6 |       end
 7 | 
 8 |       def stub_env
 9 |         {
10 |           "HAS_JOSH_K_SEAL_OF_APPROVAL" => "true",
11 |           "TRAVIS_PULL_REQUEST" => "800",
12 |           "TRAVIS_REPO_SLUG" => "artsy/eigen",
13 |           "TRAVIS_COMMIT_RANGE" => "759adcbd0d8f...13c4dc8bb61d",
14 |           "DANGER_GITHUB_API_TOKEN" => "hi"
15 |         }
16 |       end
17 | 
18 |       def stub_ci
19 |         env = { "CI_PULL_REQUEST" => "https://github.com/artsy/eigen/pull/800" }
20 |         Danger::CircleCI.new(env)
21 |       end
22 | 
23 |       def stub_request_source
24 |         Danger::RequestSources::GitHub.new(stub_ci, stub_env)
25 |       end
26 |     end
27 |   end
28 | end
29 | 


--------------------------------------------------------------------------------
/spec/support/matchers/have_instance_variables_matcher.rb:
--------------------------------------------------------------------------------
 1 | RSpec::Matchers.define(:have_instance_variables) do |expected|
 2 |   match do |actual|
 3 |     expected.each do |instance_variable, expected_value|
 4 |       expect(actual.instance_variable_get(instance_variable)).to eq(expected_value)
 5 |     end
 6 |   end
 7 | 
 8 |   failure_message do |actual|
 9 |     expected.each do |instance_variable, expected_value|
10 |       actual_value = actual.instance_variable_get(instance_variable)
11 |       if actual_value != expected_value
12 |         return "expected #{actual}#{instance_variable} to match #{expected_value.inspect}, but got #{actual_value.inspect}."
13 |       end
14 |     end
15 |   end
16 | 
17 |   failure_message_when_negated do |actual|
18 |     expected.each do |instance_variable, expected_value|
19 |       actual_value = actual.instance_variable_get(instance_variable)
20 |       if actual_value == expected_value
21 |         return "expected #{actual}#{instance_variable} not to match #{expected_value.inspect}, but got #{actual_value.inspect}."
22 |       end
23 |     end
24 |   end
25 | end
26 | 


--------------------------------------------------------------------------------
/spec/support/vsts_helper.rb:
--------------------------------------------------------------------------------
 1 | module Danger
 2 |   module Support
 3 |     module VSTSHelper
 4 |       def stub_env
 5 |         {
 6 |           "AGENT_ID" => "4a4d3fa1-bae7-4e5f-a14a-a50b184c74aa",
 7 |           "DANGER_VSTS_HOST" => "https://example.visualstudio.com/example",
 8 |           "DANGER_VSTS_API_TOKEN" => "a_token",
 9 |           "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" => "https://example.visualstudio.com",
10 |           "BUILD_SOURCEBRANCH" => "refs/pull/1/merge",
11 |           "BUILD_REPOSITORY_URI" => "https://example.visualstudio.com/_git/example",
12 |           "BUILD_REASON" => "PullRequest",
13 |           "BUILD_REPOSITORY_NAME" => "example",
14 |           "SYSTEM_TEAMPROJECT" => "example",
15 |           "SYSTEM_PULLREQUEST_PULLREQUESTNUMBER" => 1,
16 |           "BUILD_REPOSITORY_PROVIDER" => "TfsGit"
17 |         }
18 |       end
19 | 
20 |       def stub_ci
21 |         Danger::AzurePipelines.new(stub_env)
22 |       end
23 | 
24 |       def stub_request_source
25 |         Danger::RequestSources::VSTS.new(stub_ci, stub_env)
26 |       end
27 | 
28 |       def stub_pull_request
29 |         raw_file = File.new("spec/fixtures/vsts_api/pr_response.json")
30 |         url = "https://example.visualstudio.com/example/_apis/git/repositories/example/pullRequests/1?api-version=3.0"
31 |         WebMock.stub_request(:get, url).to_return(raw_file)
32 |       end
33 | 
34 |       def stub_get_comments_request_no_danger
35 |         raw_file = File.new("spec/fixtures/vsts_api/no_danger_comments_response.json")
36 |         url = "https://example.visualstudio.com/example/_apis/git/repositories/example/pullRequests/1/threads?api-version=3.0"
37 |         WebMock.stub_request(:get, url).to_return(raw_file)
38 |       end
39 | 
40 |       def stub_get_comments_request_with_danger
41 |         raw_file = File.new("spec/fixtures/vsts_api/danger_comments_response.json")
42 |         url = "https://example.visualstudio.com/example/_apis/git/repositories/example/pullRequests/1/threads?api-version=3.0"
43 |         WebMock.stub_request(:get, url).to_return(raw_file)
44 |       end
45 |     end
46 |   end
47 | end
48 | 


--------------------------------------------------------------------------------