Merge pull request #16518 from issyl0/rubocop-md

rubocop: Try out `rubocop-md` for linting the code in our docs
This commit is contained in:
Issy Long 2024-02-02 15:49:46 +00:00 committed by GitHub
commit 06028c204a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 185 additions and 141 deletions

View File

@ -40,6 +40,10 @@ jobs:
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}/docs
run: bundle exec rake lint
- name: Check code blocks conform to our Ruby style guide
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}/docs
run: brew style .
- name: Build the site and check for broken links
working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}/docs
run: |

View File

@ -1,6 +1,7 @@
---
require:
- ./Homebrew/rubocops.rb
- rubocop-md
- rubocop-performance
- rubocop-rspec
- rubocop-sorbet

View File

@ -42,6 +42,7 @@ group :pry, optional: true do
end
group :style, optional: true do
gem "rubocop", require: false
gem "rubocop-md", require: false
gem "rubocop-performance", require: false
gem "rubocop-rspec", require: false
gem "rubocop-sorbet", require: false

View File

@ -103,6 +103,8 @@ GEM
rubocop (~> 1.41)
rubocop-factory_bot (2.25.1)
rubocop (~> 1.41)
rubocop-md (1.2.2)
rubocop (>= 1.0)
rubocop-performance (1.20.2)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
@ -190,6 +192,7 @@ DEPENDENCIES
rspec_junit_formatter
rubocop
rubocop-ast
rubocop-md
rubocop-performance
rubocop-rspec
rubocop-sorbet

View File

@ -129,6 +129,8 @@ module Homebrew
files&.map!(&:expand_path)
if files.blank? || files == [HOMEBREW_REPOSITORY]
files = [HOMEBREW_LIBRARY_PATH]
elsif files.any? { |f| f.to_s.start_with? HOMEBREW_REPOSITORY/"docs" }
args << "--config" << (HOMEBREW_REPOSITORY/"docs/.rubocop.yml")
elsif files.none? { |f| f.to_s.start_with? HOMEBREW_LIBRARY_PATH }
args << "--config" << (HOMEBREW_LIBRARY/".rubocop.yml")
end

View File

@ -95,6 +95,7 @@ $:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-1.60.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-capybara-2.20.0/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-factory_bot-2.25.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-md-1.2.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-performance-1.20.2/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-rspec-2.26.1/lib")
$:.unshift File.expand_path("#{__dir__}/../#{RUBY_ENGINE}/#{Gem.ruby_api_version}/gems/rubocop-sorbet-0.7.6/lib")

32
docs/.rubocop.yml Normal file
View File

@ -0,0 +1,32 @@
inherit_from: ../Library/.rubocop.yml
AllCops:
Exclude:
- Gemfile
- ".mdl*.rb"
- Rakefile
- "_site/**/*"
- Manpage.md
# These are included in docs deliberately to show what
# `brew create` does and what the user should replace.
FormulaAudit/Comments:
Enabled: false
# This forces us to use dummy descriptions/homepages in example formulae which we don't need to clutter the docs with.
FormulaAudit/Desc:
Enabled: false
FormulaAudit/Homepage:
Enabled: false
Layout/LineLength:
Exclude:
- Bottles.md # The bottle block line length is long in its full form.
# Apparently Casks are allowed to have constant definitions in blocks and we document this.
Lint/ConstantDefinitionInBlock:
Enabled: false
# A fake regexp is deliberately documented for `inreplace` in the Formula Cookbook.
Style/RedundantRegexpArgument:
Enabled: false

View File

@ -165,36 +165,13 @@ If the `generate_cask_token` script does not work for you, see [Cask Token Detai
#### Creating the cask file
Once you know the token, create your cask with the handy-dandy `brew create --cask` command:
Once you know the token, create your cask with the `brew create --cask` command:
```bash
brew create --cask download-url --set-name my-new-cask
```
This will open `EDITOR` with a template for your new cask, to be stored in the file `my-new-cask.rb`. Running the `create` command above will get you a template that looks like this:
```ruby
cask "my-new-cask" do
version ""
sha256 ""
url "download-url"
name ""
desc ""
homepage ""
livecheck do
url ""
strategy ""
end
depends_on macos: ""
app ""
zap trash: ""
end
```
This will open `EDITOR` with a template for your new cask, to be stored in the file `my-new-cask.rb`.
#### Cask stanzas
@ -242,7 +219,7 @@ Example:
1. So, the `app` stanza should include the subfolder as a relative path:
```ruby
app "Simple Floating Clock/SimpleFloatingClock.app"
app "Simple Floating Clock/SimpleFloatingClock.app"
```
### Testing and auditing the cask

View File

@ -29,13 +29,7 @@ Exception: `do` blocks such as `postflight` may enclose a block of pure Ruby cod
## Header line details
The first non-comment line in a cask follows the form:
```ruby
cask "<cask-token>" do
```
[`<cask-token>`](#token-reference) should match the cask filename, without the `.rb` extension, enclosed in double quotes.
The Cask name ([`<cask-token>`](#token-reference)) on the header line `cask <cask-token> do` should match the cask filename, without the `.rb` extension, enclosed in double quotes.
There are currently some arbitrary limitations on cask tokens which are in the process of being removed. GitHub Actions will catch any errors during the transition.
@ -857,7 +851,7 @@ uninstall signal: [
["INT", "fr.madrau.switchresx.daemon"],
["HUP", "fr.madrau.switchresx.daemon"],
["KILL", "fr.madrau.switchresx.daemon"],
]
]
```
Note that when multiple running processes match the given bundle ID, all matching processes will be signaled.
@ -1082,7 +1076,7 @@ we can use:
```ruby
version "1.2.3"
url "https://example.com/file-version-#{version.delete('.')}.dmg"
url "https://example.com/file-version-#{version.delete(".")}.dmg"
```
We can also leverage the power of regular expressions. So instead of:
@ -1096,7 +1090,7 @@ we can use:
```ruby
version "1.2.3build4"
url "https://example.com/#{version.sub(%r{build\d+}, '')}/file-version-#{version}.dmg"
url "https://example.com/#{version.sub(/build\d+/, "")}/file-version-#{version}.dmg"
```
#### `version` methods
@ -1197,6 +1191,7 @@ cask "libreoffice" do
url "https://download.documentfoundation.org/libreoffice/stable/#{version}/mac/#{folder}/LibreOffice_#{version}_MacOS_#{arch}.dmg",
verified: "download.documentfoundation.org/libreoffice/stable/"
end
```
If the version number is different for each architecture, locate the unique `version` and (if checked) `sha256` stanzas within `on_arm` and `on_intel` blocks. Example (from [inkscape.rb](https://github.com/Homebrew/homebrew-cask/blob/11f6966bf17628b98895d64a61a4fb0bc1bb31bf/Casks/i/inkscape.rb#L1-L13)):
@ -1215,6 +1210,7 @@ cask "inkscape" do
end
url "https://inkscape.org/gallery/item/#{version.csv.second}/Inkscape-#{version.csv.first}_#{arch}.dmg"
end
```
To adjust the installed version depending on the current macOS release, use a series of `on_<system>` blocks that cover the range of supported releases. Each block can contain stanzas that set which version to download and customize installation/uninstallation and livecheck behaviour for one or more releases. Example (from [calibre.rb](https://github.com/Homebrew/homebrew-cask/blob/482c34e950da8d649705f4aaea7b760dcb4b5402/Casks/c/calibre.rb#L1-L34)):
@ -1244,6 +1240,7 @@ cask "calibre" do
strategy :github_latest
end
end
end
```
Such `on_<system>` blocks can be nested and contain other stanzas not listed here. Examples: [calhash.rb](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/c/calhash.rb), [openzfs.rb](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/o/openzfs.rb), [r.rb](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/r/r.rb), [wireshark.rb](https://github.com/Homebrew/homebrew-cask/blob/HEAD/Casks/w/wireshark.rb)
@ -1260,17 +1257,17 @@ In the exceptional case that the cask DSL is insufficient, it is possible to def
cask "myapp" do
module Utils
def self.arbitrary_method
...
# ...
end
end
name "MyApp"
version "1.0"
sha256 "a32565cdb1673f4071593d4cc9e1c26bc884218b62fef8abc450daa47ba8fa92"
url "https://#{Utils.arbitrary_method}"
name "MyApp"
homepage "https://www.example.com/"
...
# ...
end
```

View File

@ -60,33 +60,10 @@ Run `brew create` with a URL to the source tarball:
brew create https://example.com/foo-0.1.tar.gz
```
This creates `$(brew --repository)/Library/Taps/homebrew/homebrew-core/Formula/f/foo.rb` and opens it in your `EDITOR`.
Passing in `--ruby` or `--python` will populate various defaults commonly useful for projects written in those languages.
This creates `$(brew --repository)/Library/Taps/homebrew/homebrew-core/Formula/f/foo.rb` and opens it in your `EDITOR`. If run without any options to customize the output for specific build systems (check `brew create --help` to see which are available) it'll look something like:
```ruby
class Foo < Formula
desc ""
homepage ""
url "https://example.com/foo-0.1.tar.gz"
sha256 "85cc828a96735bdafcf29eb6291ca91bac846579bcef7308536e0c875d6c81d7"
license ""
# depends_on "cmake" => :build
def install
# ENV.deparallelize
system "./configure", *std_configure_args, "--disable-silent-rules"
# system "cmake", "-S", ".", "-B", "build", *std_cmake_args
system "make", "install"
end
test do
system "false"
end
end
```
If `brew` said `Warning: Version cannot be determined from URL` when doing the `create` step, youll need to explicitly add the correct [`version`](https://rubydoc.brew.sh/Formula#version-class_method) to the formula and then save the formula.
Homebrew will try to guess the formulas name from its URL. If it fails to do so you can override this with `brew create <URL> --set-name <name>`.
@ -145,14 +122,18 @@ Special exceptions are OpenSSL and LibreSSL. Things that use either *should* be
```ruby
class Foo < Formula
depends_on "pkg-config"
depends_on "jpeg"
depends_on "gtk+" => :optional
depends_on "readline" => :recommended
# ...
depends_on "httpd" => [:build, :test]
depends_on arch: :x86_64
depends_on macos: :high_sierra
depends_on xcode: ["9.3", :build]
depends_on arch: :x86_64
depends_on "jpeg"
depends_on macos: :high_sierra
depends_on "pkg-config"
depends_on "readline" => :recommended
depends_on "gtk+" => :optional
# ...
end
```
@ -168,8 +149,8 @@ A `Hash` (e.g. `=>`) adds information to a dependency. Given a string or symbol,
* `:recommended` (not allowed in `Homebrew/homebrew-core`) generates an implicit `without-foo` option, meaning that the dependency is enabled by default and the user must pass `--without-foo` to disable this dependency. The default description can be overridden using the [`option`](https://rubydoc.brew.sh/Formula#option-class_method) syntax (in this case, the [`option` declaration](#adding-optional-steps) must precede the dependency):
```ruby
option "with-foo", "Compile with foo bindings" # This overrides the generated description if you want to
depends_on "foo" => :optional # Generated description would otherwise be "Build with foo support"
option "with-foo", "Compile with foo bindings" # This overrides the generated description if you want to
depends_on "foo" => :optional # Generated description would otherwise be "Build with foo support"
```
* `"<option-name>"` (not allowed in `Homebrew/homebrew-core`) requires a dependency to have been built with the specified option.
@ -271,7 +252,7 @@ And install any bins, and munge their shebang lines, with:
```ruby
bin.install libexec/"bin/<bin>"
bin.env_script_all_files(libexec/"bin", GEM_HOME: ENV["GEM_HOME"])
bin.env_script_all_files(libexec/"bin", GEM_HOME: ENV.fetch("GEM_HOME", nil))
```
### Python dependencies
@ -284,6 +265,9 @@ If all else fails, you'll want to use [`resource`](https://rubydoc.brew.sh/Formu
```ruby
class Foo < Formula
# ...
url "https://example.com/foo-1.0.tar.gz"
resource "pycrypto" do
url "https://files.pythonhosted.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz"
sha256 "f2ce1e989b272cfcb677616763e0a2e7ec659effa67a88aa92b3a65528f60a3c"
@ -546,7 +530,7 @@ end
```ruby
stable do
# some other things...
# ...
patch do
url "https://example.com/example_patch.diff"
@ -662,6 +646,7 @@ Formulae can specify an alternate download for the upstream projects developm
```ruby
class Foo < Formula
# ...
head "https://github.com/some/package.git", branch: "main" # the default is "master"
end
```
@ -670,6 +655,8 @@ You can also bundle the URL and any `head`-specific dependencies and resources i
```ruby
class Foo < Formula
# ...
head do
url "https://svn.code.sf.net/p/project/code/trunk"
depends_on "pkg-config" => :build
@ -685,7 +672,7 @@ When parsing a download URL, Homebrew auto-detects the resource type it points t
```ruby
class Foo < Formula
homepage "https://github.com/some/package"
# ...
url "https://github.com/some/package.git",
tag: "v1.6.2",
revision: "344cd2ee3463abab4c16ac0f9529a846314932a2"
@ -696,10 +683,12 @@ If not inferable, specify which of Homebrews built-in download strategies to
```ruby
class Nginx < Formula
desc "HTTP(S) server and reverse proxy, and IMAP/POP3 proxy server"
homepage "https://nginx.org/"
url "https://nginx.org/download/nginx-1.23.2.tar.gz", using: :homebrew_curl
sha256 "a80cc272d3d72aaee70aa8b517b4862a635c0256790434dbfc4d618a999b0b46"
head "https://hg.nginx.org/nginx/", using: :hg
end
```
Homebrew offers these anonymous download strategies.
@ -729,7 +718,7 @@ class MyDownloadStrategy < SomeHomebrewDownloadStrategy
end
class Foo < Formula
url "something", :using => MyDownloadStrategy
url "something", using: MyDownloadStrategy
end
```
@ -855,25 +844,25 @@ Several other utilities for Ruby's [`Pathname`](https://rubydoc.brew.sh/Pathname
* To perform several operations within a directory, enclose them within a [`cd <path> do`](https://rubydoc.brew.sh/Pathname#cd-instance_method) block:
```ruby
cd "src" do
cd "src" do
system "./configure", "--disable-debug", "--prefix=#{prefix}"
system "make", "install"
end
end
```
* To surface one or more binaries buried in `libexec` or a macOS `.app` package, use [`write_exec_script`](https://rubydoc.brew.sh/Pathname#write_exec_script-instance_method) or [`write_jar_script`](https://rubydoc.brew.sh/Pathname#write_jar_script-instance_method):
```ruby
bin.write_exec_script (libexec/"bin").children
bin.write_exec_script prefix/"Package.app/Contents/MacOS/package"
bin.write_jar_script libexec/jar_file, "jarfile", java_version: "11"
bin.write_exec_script (libexec/"bin").children
bin.write_exec_script prefix/"Package.app/Contents/MacOS/package"
bin.write_jar_script libexec/jar_file, "jarfile", java_version: "11"
```
* For binaries that require setting one or more environment variables to function properly, use [`write_env_script`](https://rubydoc.brew.sh/Pathname#write_env_script-instance_method) or [`env_script_all_files`](https://rubydoc.brew.sh/Pathname#env_script_all_files-instance_method):
```ruby
(bin/"package").write_env_script libexec/"package", PACKAGE_ROOT: libexec
bin.env_script_all_files(libexec/"bin", PERL5LIB: ENV["PERL5LIB"])
(bin/"package").write_env_script libexec/"package", PACKAGE_ROOT: libexec
bin.env_script_all_files(libexec/"bin", PERL5LIB: ENV.fetch("PERL5LIB", nil))
```
### Rewriting a script shebang
@ -890,13 +879,17 @@ If you want to add an [`option`](https://rubydoc.brew.sh/Formula#option-class_me
```ruby
class Yourformula < Formula
...
# ...
url "https://example.com/yourformula-1.0.tar.gz"
sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1"
# ...
option "with-ham", "Description of the option"
option "without-spam", "Another description"
depends_on "foo" => :optional # automatically adds a with-foo option
depends_on "bar" => :recommended # automatically adds a without-bar option
...
depends_on "bar" => :recommended
depends_on "foo" => :optional # automatically adds a with-foo option # automatically adds a without-bar option
# ...
end
```
And then to define the effects the [`option`](https://rubydoc.brew.sh/Formula#option-class_method)s have:
@ -920,9 +913,15 @@ end
Any initialization steps that aren't necessarily part of the install process can be located in a `post_install` block, such as setup commands or data directory creation. This block can be re-run separately with `brew postinstall <formula>`.
```ruby
def post_install
class Foo < Formula
# ...
url "https://example.com/foo-1.0.tar.gz"
def post_install
rm_f pkgetc/"cert.pem"
pkgetc.install_symlink Formula["ca-certificates"].pkgetc/"cert.pem"
end
# ...
end
```
@ -941,10 +940,10 @@ There are two ways to add `launchd` plists and `systemd` services to a formula,
1. If the package already provides a service file the formula can reference it by name:
```ruby
service do
service do
name macos: "custom.launchd.name",
linux: "custom.systemd.name"
end
end
```
To find the file we append `.plist` to the `launchd` service name and `.service` to the `systemd` service name internally.
@ -953,20 +952,20 @@ There are two ways to add `launchd` plists and `systemd` services to a formula,
```ruby
# 1. An individual command
service do
service do
run opt_bin/"script"
end
end
# 2. A command with arguments
service do
service do
run [opt_bin/"script", "--config", etc/"dir/config.yml"]
end
end
# 3. OS specific commands (If you omit one, the service file won't get generated for that OS.)
service do
service do
run macos: [opt_bin/"macos_script", "standalone"],
linux: var/"special_linux_script"
end
end
```
#### Service block methods

View File

@ -68,8 +68,8 @@ These expressions can be nested as needed:
license any_of: [
"MIT",
:public_domain,
all_of: ["0BSD", "Zlib", "Artistic-1.0+"],
"Apache-2.0" => { with: "LLVM-exception" },
{ all_of: ["0BSD", "Zlib", "Artistic-1.0+"],
"Apache-2.0" => { with: "LLVM-exception" } },
]
```

View File

@ -91,10 +91,10 @@ Installing a standard Node module based formula would look like this:
require "language/node"
class Foo < Formula
desc "..."
homepage "..."
desc "An example formula"
homepage "https://example.com"
url "https://registry.npmjs.org/foo/-/foo-1.4.2.tgz"
sha256 "..."
sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1"
depends_on "node"
# uncomment if there is a native addon inside the dependency tree
@ -106,7 +106,8 @@ class Foo < Formula
end
test do
# add a meaningful test here
# add a meaningful test here, version isn't usually meaningful
assert_match version.to_s, shell_output("#{bin}/foo --version")
end
end
```

View File

@ -50,15 +50,32 @@ Homebrew provides helper methods for instantiating and populating virtualenvs. Y
For most applications, all you will need to write is:
```ruby
def install
class Foo < Formula
include Language::Python::Virtualenv
name "foo"
# ...
url "..."
sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1"
def install
virtualenv_install_with_resources
end
end
```
This is exactly the same as writing:
```ruby
def install
class Foo < Formula
include Language::Python::Virtualenv
name "foo"
# ...
url "https://example.com/foo-1.0.tar.gz"
sha256 "abc123abc123abc123abc123abc123abc123abc123abc123abc123abc123abc1"
def install
# Create a virtualenv in `libexec`. If your app needs Python 3, make sure that
# `depends_on "python"` is declared, and use `virtualenv_create(libexec, "python3")`.
venv = virtualenv_create(libexec)
@ -70,6 +87,7 @@ def install
# that the formula points to, because buildpath is the location where the
# formula's tarball was unpacked.
venv.pip_install_and_link buildpath
end
end
```
@ -81,6 +99,8 @@ Installing a formula with dependencies will look like this:
class Foo < Formula
include Language::Python::Virtualenv
desc "Description"
homepage "https://example.com"
url "..."
resource "six" do
@ -102,12 +122,20 @@ end
You can also use the more verbose form and request that specific resources be installed:
```ruby
def install
class Foo < Formula
include Language::Python::Virtualenv
desc "Description"
homepage "https://example.com"
url "..."
def install
venv = virtualenv_create(libexec)
%w[six parsedatetime].each do |r|
venv.pip_install resource(r)
end
venv.pip_install_and_link buildpath
end
end
```

View File

@ -12,8 +12,6 @@ The `sig` method is used to annotate method signatures. Here's a simple example:
```ruby
class MyClass
extend T::Sig
sig { params(name: String).returns(String) }
def my_method(name)
"Hello, #{name}!"