crawlscope 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c7c5616af9b17e185b54274dfa07997d712f93ae37285d1cc9dbe190d289882
4
- data.tar.gz: a443e0cfdce5c3a1a08bdcc0b53916a6e60631aad5c3ef5c9cc5377e70558383
3
+ metadata.gz: 18717d5f025a5e0dc10b3f5a231b52d788a7ad09fe017b803815dc1cf22b7f11
4
+ data.tar.gz: 25f80cac563922d3c34d069c34b6c5521064fb4a74dd98cc54f6bb04e6de283a
5
5
  SHA512:
6
- metadata.gz: f00dbb412e2372bbe757b7fdf84dadc5e7cb05f82f2e254a08a5b8a199020b3cbdceff460ecf37f53a93d9617c769be1673120599edade1f8e9881c5a2a56de7
7
- data.tar.gz: 462cce5554b2872955c76855bf60a77e93948c1162219ec129b7d3a5e511a3726b73157d33db8a4b9c3a64564e2c69306138b9c0ff62600b67570e1d6fee0403
6
+ metadata.gz: 322dacf831bfa19dd16a6ebf3917e8d8bcaab462b440468945e4e9e07768a42955afe7f418b217594f9654c22490d32124f6fdb95004a20f23508f76bca6d7db
7
+ data.tar.gz: b301ad0f2c7c92dd3dc436f7019c62d7958bdd402eb8a887626d938a9bfd085d8d21bdab76a5243f3e2d9684d94ffce554e5108db26995d67b63140fcadbb9e2
data/CHANGELOG.md CHANGED
@@ -5,6 +5,24 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.2] - 2026-07-23
9
+
10
+
11
+ ### Fixed
12
+
13
+ - preserve host configuration
14
+
15
+
16
+
17
+ ## [0.7.1] - 2026-07-21
18
+
19
+
20
+ ### Added
21
+
22
+ - add lazy install generator
23
+
24
+
25
+
8
26
  ## [0.7.0] - 2026-07-21
9
27
 
10
28
 
data/README.md CHANGED
@@ -142,24 +142,23 @@ puts result.issues.to_a.map(&:message)
142
142
  - `pages`: fetched page snapshots
143
143
  - `issues`: structured issues with `code`, `severity`, `category`, `url`, and `message`
144
144
 
145
- `result.ok?` returns `false` if any error, warning, or notice is present.
145
+ `result.ok?` returns `false` when an error is present. Warnings and notices
146
+ remain available through `result.issues` without making the result fail.
146
147
 
147
148
  ## Rails Usage
148
149
 
149
- For an app that uses Crawlscope through rake tasks, load the task entrypoint
150
- only when a Crawlscope task is requested. In the application's `Rakefile`,
151
- before `require_relative "config/application"`:
150
+ Run the install generator after adding the gem:
152
151
 
153
- ```ruby
154
- list_tasks = Rake.application.options.show_tasks || ARGV.any? { |argument| ["-T", "--tasks"].include?(argument) }
155
- require "crawlscope/tasks" if list_tasks || ARGV.any? { |argument| argument.start_with?("crawlscope:") }
152
+ ```bash
153
+ bin/rails generate crawlscope:install
156
154
  ```
157
155
 
158
- This preserves `bin/rails --tasks` and the `crawlscope:*` tasks without loading
159
- Crawlscope during normal Rails boot. Runtime callers should explicitly
160
- `require "crawlscope"` immediately before configuring or using the gem.
156
+ The generator creates `config/initializers/crawlscope.rb` with an idempotent
157
+ `CrawlscopeConfiguration.apply` loader and adds conditional `crawlscope/tasks`
158
+ loading to the application's `Rakefile`. This preserves `bin/rails --tasks` and
159
+ the `crawlscope:*` tasks without loading Crawlscope during normal Rails boot.
161
160
 
162
- In an initializer:
161
+ Customize the `Crawlscope.configure` block inside the generated initializer:
163
162
 
164
163
  ```ruby
165
164
  Crawlscope.configure do |config|
@@ -170,6 +169,20 @@ Crawlscope.configure do |config|
170
169
  end
171
170
  ```
172
171
 
172
+ Runtime callers must apply the generated configuration before using
173
+ Crawlscope:
174
+
175
+ ```ruby
176
+ CrawlscopeConfiguration.apply
177
+ Crawlscope.configuration.audit
178
+ ```
179
+
180
+ Rake tasks apply it automatically because `crawlscope/tasks` loads the gem
181
+ before Rails evaluates the initializer. Each Rake entry point passes the shared
182
+ `Crawlscope.configuration` object to the CLI, so configured base URLs, sitemap
183
+ paths, registries, and runtime settings are preserved unless a task argument or
184
+ environment override replaces them.
185
+
173
186
  Then run:
174
187
 
175
188
  ```bash
@@ -211,7 +224,12 @@ bundle exec rake crawlscope:validate:ldjson URL=https://example.com/article
211
224
  bundle exec rake 'crawlscope:validate:ldjson[https://example.com/article]'
212
225
  ```
213
226
 
214
- `crawlscope:validate` runs all default sitemap rules: indexability, metadata, structured data, uniqueness, content quality, and links. `URL` is the site base. Without `SITEMAP`, Crawlscope uses `/sitemap.xml`. With `SITEMAP`, Crawlscope uses `URL` as the site base and validates URLs from that sitemap. `SITEMAP` may be a full URL or a local file path.
227
+ `crawlscope:validate` runs all default sitemap rules: indexability, metadata,
228
+ structured data, uniqueness, content quality, and links. `URL` is the site
229
+ base. Without `SITEMAP`, Crawlscope uses the configured sitemap path, then
230
+ falls back to `/sitemap.xml`. With `SITEMAP`, Crawlscope uses `URL` as the site
231
+ base and validates URLs from that sitemap. `SITEMAP` may be a full URL or a
232
+ local file path.
215
233
 
216
234
  Plain `rake` does not pass `--url` style flags to tasks. Use `URL=...` or the
217
235
  task-argument form above instead.
@@ -16,8 +16,8 @@ module Crawlscope
16
16
  validate(url: url, sitemap_path: sitemap_path, rule_names: rule)
17
17
  end
18
18
 
19
- def run(command, argv: [])
20
- status = Cli.start([command, *argv], out: $stdout, err: $stderr)
19
+ def run(command, argv: [], configuration: Crawlscope.configuration)
20
+ status = Cli.start([command, *argv], out: $stdout, err: $stderr, configuration: configuration)
21
21
  exit(status) unless status.zero?
22
22
  end
23
23
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crawlscope
4
- VERSION = "0.7.0"
4
+ VERSION = "0.7.2"
5
5
  end
data/lib/crawlscope.rb CHANGED
@@ -27,6 +27,7 @@ module Crawlscope
27
27
  end
28
28
 
29
29
  Crawlscope.instance_variable_set(:@loader, Zeitwerk::Loader.for_gem)
30
+ Crawlscope.loader.ignore("#{__dir__}/generators")
30
31
  Crawlscope.loader.ignore("#{__dir__}/tasks")
31
32
  Crawlscope.loader.ignore("#{__dir__}/crawlscope/railtie.rb")
32
33
  Crawlscope.loader.ignore("#{__dir__}/crawlscope/tasks.rb")
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Crawlscope
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ desc "Create a lazy-safe Crawlscope initializer and task loader"
11
+
12
+ def create_initializer
13
+ template "initializer.rb.tt", "config/initializers/crawlscope.rb"
14
+ end
15
+
16
+ def configure_rakefile
17
+ rakefile = File.join(destination_root, "Rakefile")
18
+ return unless File.exist?(rakefile)
19
+ return if File.read(rakefile).include?('require "crawlscope/tasks"')
20
+
21
+ inject_into_file "Rakefile", rakefile_setup, before: /^require_relative ["']config\/application["']/
22
+ end
23
+
24
+ private
25
+
26
+ def rakefile_setup
27
+ <<~RUBY
28
+ crawlscope_tasks_requested = Rake.application.options.show_tasks ||
29
+ ARGV.any? { |argument| ["-T", "--tasks"].include?(argument) }
30
+ require "crawlscope/tasks" if crawlscope_tasks_requested ||
31
+ ARGV.any? { |argument| argument.start_with?("crawlscope:") }
32
+
33
+ RUBY
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CrawlscopeConfiguration
4
+ MUTEX = Mutex.new
5
+
6
+ def self.apply
7
+ return if @applied
8
+
9
+ MUTEX.synchronize do
10
+ return if @applied
11
+
12
+ require "crawlscope"
13
+
14
+ Crawlscope.configure do |config|
15
+ config.base_url = -> { ENV.fetch("CRAWLSCOPE_BASE_URL", "http://localhost:3000") }
16
+ config.sitemap_path = lambda {
17
+ ENV.fetch("SITEMAP", Rails.public_path.join("sitemap.xml").to_s)
18
+ }
19
+ config.site_name = ENV.fetch("CRAWLSCOPE_SITE_NAME", "Application")
20
+ end
21
+
22
+ @applied = true
23
+ end
24
+ end
25
+ end
26
+
27
+ CrawlscopeConfiguration.apply if defined?(Crawlscope)
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+ require "generators/crawlscope/install_generator"
5
+ require "open3"
6
+ require "rbconfig"
7
+
8
+ class CrawlscopeInstallGeneratorTest < Minitest::Test
9
+ def test_generates_lazy_initializer_and_task_loader
10
+ Dir.mktmpdir do |destination|
11
+ File.write(File.join(destination, "Rakefile"), <<~RUBY)
12
+ require_relative "config/application"
13
+ Rails.application.load_tasks
14
+ RUBY
15
+
16
+ Crawlscope::Generators::InstallGenerator.start([], destination_root: destination)
17
+
18
+ initializer = File.join(destination, "config/initializers/crawlscope.rb")
19
+ rakefile = File.read(File.join(destination, "Rakefile"))
20
+
21
+ assert File.exist?(initializer)
22
+ assert_includes File.read(initializer), "module CrawlscopeConfiguration"
23
+ assert_includes File.read(initializer), "CrawlscopeConfiguration.apply if defined?(Crawlscope)"
24
+ assert_includes rakefile, 'require "crawlscope/tasks"'
25
+ assert_operator rakefile.index('require "crawlscope/tasks"'), :<, rakefile.index('require_relative "config/application"')
26
+
27
+ _stdout, stderr, status = Open3.capture3(RbConfig.ruby, initializer)
28
+ assert status.success?, stderr
29
+
30
+ script = <<~RUBY
31
+ require "pathname"
32
+
33
+ module Rails
34
+ def self.public_path
35
+ Pathname.new("public")
36
+ end
37
+ end
38
+
39
+ require "crawlscope"
40
+ load ARGV.fetch(0)
41
+
42
+ configuration = Crawlscope.configuration
43
+ CrawlscopeConfiguration.apply
44
+ abort "configuration replaced" unless Crawlscope.configuration.equal?(configuration)
45
+ abort "base URL missing" unless configuration.base_url == "http://localhost:3000"
46
+ abort "sitemap missing" unless configuration.sitemap_path == "public/sitemap.xml"
47
+ RUBY
48
+ _stdout, stderr, status = Open3.capture3(
49
+ RbConfig.ruby,
50
+ "-I#{File.expand_path("../../lib", __dir__)}",
51
+ "-e",
52
+ script,
53
+ initializer
54
+ )
55
+ assert status.success?, stderr
56
+ end
57
+ end
58
+ end
@@ -3,6 +3,8 @@
3
3
  require "test_helper"
4
4
 
5
5
  class CrawlscopeRakeTasksTest < Minitest::Test
6
+ Rule = Data.define(:code)
7
+
6
8
  def setup
7
9
  @original_start = Crawlscope::Cli.method(:start)
8
10
  end
@@ -13,6 +15,7 @@ class CrawlscopeRakeTasksTest < Minitest::Test
13
15
  singleton_class.define_method(:start) do |*args, **kwargs|
14
16
  original_start.call(*args, **kwargs)
15
17
  end
18
+ Crawlscope.reset!
16
19
  end
17
20
 
18
21
  def test_validate_passes_rake_arguments_to_cli
@@ -56,14 +59,43 @@ class CrawlscopeRakeTasksTest < Minitest::Test
56
59
  )
57
60
  end
58
61
 
62
+ def test_run_passes_the_shared_configuration_to_cli
63
+ calls = capture_cli_calls
64
+ configuration = Crawlscope.configuration
65
+
66
+ Crawlscope::RakeTasks.run("validate")
67
+
68
+ assert_same configuration, calls.fetch(0).fetch(:kwargs).fetch(:configuration)
69
+ end
70
+
71
+ def test_validate_passes_the_host_rule_registry_to_cli
72
+ registry = Crawlscope::RuleRegistry.new(rules: [Rule.new(:host)], default_codes: [:host])
73
+ Crawlscope.configure { |configuration| configuration.rule_registry = registry }
74
+ calls = capture_cli_calls
75
+
76
+ Crawlscope::RakeTasks.validate
77
+
78
+ call = calls.fetch(0)
79
+ assert_equal "validate", call.fetch(:argv).first
80
+ assert_same registry, call.fetch(:kwargs).fetch(:configuration).rule_registry
81
+ end
82
+
83
+ def test_run_exits_with_a_nonzero_cli_status
84
+ capture_cli_calls(status: 2)
85
+
86
+ error = assert_raises(SystemExit) { Crawlscope::RakeTasks.run("validate") }
87
+
88
+ assert_equal 2, error.status
89
+ end
90
+
59
91
  private
60
92
 
61
- def capture_cli_calls
93
+ def capture_cli_calls(status: 0)
62
94
  calls = []
63
95
  singleton_class = class << Crawlscope::Cli; self; end
64
96
  singleton_class.define_method(:start) do |argv, **kwargs|
65
97
  calls << {argv: argv, kwargs: kwargs}
66
- 0
98
+ status
67
99
  end
68
100
  calls
69
101
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: crawlscope
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Paulo Fidalgo
@@ -259,6 +259,8 @@ files:
259
259
  - lib/crawlscope/tasks.rb
260
260
  - lib/crawlscope/url.rb
261
261
  - lib/crawlscope/version.rb
262
+ - lib/generators/crawlscope/install_generator.rb
263
+ - lib/generators/crawlscope/templates/initializer.rb.tt
262
264
  - lib/tasks/crawlscope_tasks.rake
263
265
  - test/crawlscope/browser_test.rb
264
266
  - test/crawlscope/cli_test.rb
@@ -269,6 +271,7 @@ files:
269
271
  - test/crawlscope/fetch_executor_test.rb
270
272
  - test/crawlscope/http_test.rb
271
273
  - test/crawlscope/indexability_rule_test.rb
274
+ - test/crawlscope/install_generator_test.rb
272
275
  - test/crawlscope/links_rule_test.rb
273
276
  - test/crawlscope/loader_test.rb
274
277
  - test/crawlscope/metadata_rule_test.rb