New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Created a plugin documentation parser in Danger #227

Merged
merged 3 commits into from Jun 1, 2016

Conversation

Projects
None yet
3 participants
@orta
Member

orta commented Jun 1, 2016

@KrauseFx was correct to be suspicious when I introduced the revised plugin support ( #223 ) it was a significant change from all of our exist discussion about what a plugin could look like.

If there's anything I've learned from 90k CocoaPods versions and from managing CocoaPods, it's that the initial expectations will stick with you for years. I need Danger to stay small, because I don't have time to product manage another project as big as Danger could become. So, Danger needs to be extremely easy to build a plugin for.

I think there's two spaces for plugins, private plugins ( like we have in Danger - protect_files and some of the things SoundCloud do ) and public plugins. A private plugin can be a quick throwaway class, easy to iterate, and you can copy & paste some code directly from your Dangerfile into a plugin template, and you're good.

Public Plugins however need to hold themselves to a higher standard. In order for Danger to be seen as more than "just and iOS project" and applicable to basically every project using code review, the website has to really showcase a lot of interesting use cases. Public plugins can provide this.

This was what I had in mind when I started roughing the design of what plugin support should look like:

screen shot 2016-05-31 at 9 41 57 pm

The usage of CocoaPods sky-rocketed once I re-designed the website with search in mind. Since then most (updated) package managers have taken similar approaches to their sites. It's not novel, it's a simple approach based on our analytics. In the future, Danger's plugin search may move to the top of the site.

To do this right, we need to be able to document public plugins easily. rubydocs won't do in this case. It's too opaque for non-rubyists, so we need to pull out the metadata we need and convert that into something we can work with. This is a pattern @fabiopelosin used in the CocoaPods guides, we pull all the useful data out of the ruby code. It's how our references are built. We'll be using this in http://danger.systems.

So, this provides the kernel of the idea, it is a class that maps a plugin into a hash. We can then build a linter for public documentation, and well, the documentation engine. Really glad to be feeling close.

Example:

This JSON:


[
  {
    "name": "DangerProselint",
    "body_md": "Lint markdown files inside your projects.\nThis is done using the [proselint](http:\/\/proselint.com) python egg.\nResults are passed out as a table in markdown.",
    "example_code": [
      "\n# Runs a linter with comma style disabled\nproselint.disable_linters = [\"misc.scare_quotes\", \"misc.tense_present\"]\nproselint.lint_files \"_posts\/*.md\"\n\n# Runs a linter with all styles, on modified and added markpown files in this PR\nproselint.lint_files"
    ],
    "attributes": [
      {
        "disable_linters": {
          "read": null,
          "write": {
            "name": "disable_linters=",
            "body_md": "Allows you to disable a collection of linters from being ran.\nYou can get a list of [them here](https:\/\/github.com\/amperser\/proselint#checks)",
            "tags": [

            ]
          }
        }
      }
    ],
    "methods": [
      {
        "name": "disable_linters=",
        "body_md": "Allows you to disable a collection of linters from being ran.\nYou can get a list of [them here](https:\/\/github.com\/amperser\/proselint#checks)",
        "tags": [

        ]
      },
      {
        "name": "lint_files",
        "body_md": "Lints the globbed files, which can fail your build if",
        "tags": [
          {
            "name": "param",
            "types": [
              "String"
            ]
          },
          {
            "name": "return",
            "types": [
              "void"
            ]
          }
        ]
      },
      {
        "name": "proselint_installed?",
        "body_md": "Determine if proselint is currently installed in the system paths.",
        "tags": [
          {
            "name": "return",
            "types": [
              "Bool"
            ]
          }
        ]
      }
    ],
    "tags": [

    ],
    "see": [
      "artsy\/artsy.github.io"
    ],
    "file": "spec\/fixtures\/plugins\/example_full_plugin.rb"
  }
]

Represents this Plugin:

module Danger
  # Lint markdown files inside your projects.
  # This is done using the [proselint](http://proselint.com) python egg.
  # Results are passed out as a table in markdown.
  #
  # @example Specifying custom CocoaPods installation options
  #
  #          # Runs a linter with comma style disabled
  #          proselint.disable_linters = ["misc.scare_quotes", "misc.tense_present"]
  #          proselint.lint_files "_posts/*.md"
  #
  #          # Runs a linter with all styles, on modified and added markpown files in this PR
  #          proselint.lint_files
  #
  # @see     artsy/artsy.github.io
  # @tags    blogging, blog, writing, jekyll, middleman, hugo, metalsmith, gatsby, express
  #
  class DangerProselint < Plugin
    # Allows you to disable a collection of linters from being ran.
    # You can get a list of [them here](https://github.com/amperser/proselint#checks)
    attr_writer :disable_linters

    # Lints the globbed files, which can fail your build if
    #
    # @param   [String] files
    #          A globbed string which should return the files that you want to lint, defaults to nil.
    #          if nil, modified and added files will be used.
    # @return  [void]
    #
    def lint_files(files = nil)
      # Installs a prose checker if needed
      system "pip install --user proselint" unless proselint_installed?

      # Check that this is in the user's PATH
      if `which proselint`.strip.empty?
        fail "proselint is not in the user's PATH, or it failed to install"
      end

      # Either use files provided, or use the modified + added
      markdown_files = files ? Dir.glob(files) : (modified_files + added_files)
      markdown_files.select! { |line| line.end_with?(".markdown", ".md") }

      # TODO: create the disabled linters JSON in ~/.proselintrc
      # using @disable_linter

      # Convert paths to proselint results
      require 'json'
      result_jsons = Hash[markdown_files.uniq.collect { |v| [v, JSON.parse(`proselint #{v} --json`.strip)] }]
      proses = result_jsons.select { |path, prose| prose['data']['errors'].count }

      # Get some metadata about the local setup
      current_branch = env.request_source.pr_json["head"]["ref"]
      current_slug = env.ci_source.repo_slug

      # We got some error reports back from proselint
      if proses.count > 0
        message = "### Proselint found issues\n\n"
        proses.each do |path, prose|
          github_loc = "/#{current_slug}/tree/#{current_branch}/#{path}"
          message << "#### [#{path}](#{github_loc})\n\n"

          message << "Line | Message | Severity |\n"
          message << "| --- | ----- | ----- |\n"

          prose["data"]["errors"].each do |error|
            message << "#{error['line']} | #{error['message']} | #{error['severity']}\n"
          end
        end

        markdown message
      end
    end

    # Determine if proselint is currently installed in the system paths.
    # @return  [Bool]
    #
    def proselint_installed?
      `which proselint`.strip.empty?
    end
  end
end

At some point I will do a larger scale post on how working on CocoaPods infrastructure affected Danger

@DangerCI

This comment has been minimized.

Show comment
Hide comment
@DangerCI

DangerCI Jun 1, 2016

      <tr>
    <td>:white_check_mark:</td>
    <td data-sticky="true"><del><p>Please include a CHANGELOG entry. 

You can find it at CHANGELOG.md.


        :white_check_mark: Jolly good show.
    
  </th>
 </tr>
        1 Warning
    
  </th>
 </tr>
⚠️ .gemspec modified

Generated by 🚫 danger

DangerCI commented Jun 1, 2016

      <tr>
    <td>:white_check_mark:</td>
    <td data-sticky="true"><del><p>Please include a CHANGELOG entry. 

You can find it at CHANGELOG.md.


        :white_check_mark: Jolly good show.
    
  </th>
 </tr>
        1 Warning
    
  </th>
 </tr>
⚠️ .gemspec modified

Generated by 🚫 danger

@orta orta merged commit 99723d8 into master Jun 1, 2016

3 checks passed

ci/circleci Your tests passed on CircleCI!
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
continuous-integration/travis-ci/push The Travis CI build passed
Details

@orta orta deleted the plugin_lint branch Jun 1, 2016

@KrauseFx

This comment has been minimized.

Show comment
Hide comment
@KrauseFx

KrauseFx Jun 1, 2016

Member

OMG YES 🚀 🎉

Member

KrauseFx commented Jun 1, 2016

OMG YES 🚀 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment