crawlscope 0.10.0 → 0.10.1
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 +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +51 -260
- data/docs/rails.md +121 -0
- data/docs/rules.md +95 -0
- data/docs/server-timing.md +59 -0
- data/lib/crawlscope/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1ff240a395648f3047e4fd24fe2728ad5c7ad3c22406029f7dbb0316f43bee73
|
|
4
|
+
data.tar.gz: 93e012e28037929afadba5df00f8ad441665ddda8cbc55775278fcc427d5af00
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d7f51921917c37fd1cb6576d1c1c56957cdc6be141906b5958153c548296248a76316832efd7dcd562835a08b6393a3009e577ed59f676b72786ec3dc99cd81b
|
|
7
|
+
data.tar.gz: 7db8cd080f1256e42e17cf09cbd3a34f2a62bcbd0e48238e58f420359519fd4e737516c21ad48212c17bc34e093914e641d2a8fbbd60ec94f68600466ae098bf
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ 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.10.1] - 2026-11-08
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Documentation
|
|
12
|
+
|
|
13
|
+
- make Ethos Link the canonical documentation home
|
|
14
|
+
|
|
15
|
+
- keep audited technical guidance alongside the code
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
8
19
|
## [0.10.0] - 2026-11-06
|
|
9
20
|
|
|
10
21
|
|
data/README.md
CHANGED
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
[](https://badge.fury.io/rb/crawlscope)
|
|
4
4
|
[](https://github.com/ethos-link/crawlscope/actions/workflows/commitlint.yml)
|
|
5
5
|
|
|
6
|
-
`crawlscope`
|
|
6
|
+
`crawlscope` audits sitemap URLs for indexability, metadata, structured data and internal links.
|
|
7
7
|
|
|
8
8
|
It is built by [Ethos Link](https://www.ethos-link.com) and used in production by [Reviato](https://www.reviato.com).
|
|
9
9
|
|
|
10
10
|
It is designed for Rails apps and plain Ruby scripts that want:
|
|
11
11
|
|
|
12
|
-
-
|
|
12
|
+
- sitemap-driven crawling
|
|
13
13
|
- structured validation issues instead of free-form strings
|
|
14
14
|
- app-configurable rule and schema registries
|
|
15
15
|
- first-party rake tasks instead of a large DSL
|
|
@@ -30,6 +30,16 @@ The default rule set includes:
|
|
|
30
30
|
- content-quality checks
|
|
31
31
|
- internal-link checks
|
|
32
32
|
|
|
33
|
+
## Documentation
|
|
34
|
+
|
|
35
|
+
This README and the linked repository guides describe the checked-out code.
|
|
36
|
+
The [Ethos Link project page](https://www.ethos-link.com/opensource/crawlscope)
|
|
37
|
+
provides the canonical public project guide, overview and resources.
|
|
38
|
+
|
|
39
|
+
- [Rails integration and tasks](docs/rails.md)
|
|
40
|
+
- [Rule catalogue and schema registries](docs/rules.md)
|
|
41
|
+
- [Server timing and diagnostic headers](docs/server-timing.md)
|
|
42
|
+
|
|
33
43
|
## Installation
|
|
34
44
|
|
|
35
45
|
Crawlscope requires Ruby 4.0 or newer. Rails is optional; the CLI and Ruby
|
|
@@ -63,7 +73,7 @@ If you want browser rendering, also add:
|
|
|
63
73
|
gem "ferrum"
|
|
64
74
|
```
|
|
65
75
|
|
|
66
|
-
`crawlscope` only loads Ferrum when you run in browser mode.
|
|
76
|
+
`crawlscope` only loads Ferrum when you run in browser mode. Install a compatible Chrome or Chromium executable as well.
|
|
67
77
|
|
|
68
78
|
## CLI Usage
|
|
69
79
|
|
|
@@ -163,277 +173,46 @@ puts result.issues.to_a.map(&:message)
|
|
|
163
173
|
`result.ok?` returns `false` when an error is present. Warnings and notices
|
|
164
174
|
remain available through `result.issues` without making the result fail.
|
|
165
175
|
|
|
166
|
-
##
|
|
167
|
-
|
|
168
|
-
Crawlscope parses the
|
|
169
|
-
[`Server-Timing`](https://www.w3.org/TR/server-timing/) response header for both
|
|
170
|
-
HTTP and browser-rendered crawls. Each page exposes its parsed header through
|
|
171
|
-
`page.server_timing`:
|
|
172
|
-
|
|
173
|
-
```ruby
|
|
174
|
-
page.server_timing.each do |metric|
|
|
175
|
-
puts [metric.name, metric.duration, metric.description].compact.join(": ")
|
|
176
|
-
end
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
The validation report adds a `Server Timing` section only when at least one page
|
|
180
|
-
publishes the header. It includes:
|
|
181
|
-
|
|
182
|
-
- header coverage and the number of pages publishing durations
|
|
183
|
-
- sample and page counts with average, p50, p95, and maximum per metric
|
|
184
|
-
- non-duration signals, including cache status and routing descriptions
|
|
185
|
-
- the ten pages with the largest individual metric
|
|
186
|
-
- the number of malformed entries ignored during parsing
|
|
187
|
-
|
|
188
|
-
`dur` values are reported as milliseconds, as recommended by the specification.
|
|
189
|
-
Crawlscope does not add durations together because metrics such as `total`,
|
|
190
|
-
`app`, and `db` may overlap. A page's worst-offender entry is its largest
|
|
191
|
-
individual metric instead.
|
|
192
|
-
|
|
193
|
-
The current HTTP and browser transports expose response headers, not response
|
|
194
|
-
trailers. Metrics published only in a `Server-Timing` trailer are therefore not
|
|
195
|
-
available to Crawlscope.
|
|
176
|
+
## Rails integration
|
|
196
177
|
|
|
197
|
-
|
|
178
|
+
Add `gem "crawlscope", require: false` to the host application's Gemfile, run
|
|
179
|
+
`bundle install`, then install the task/configuration integration:
|
|
198
180
|
|
|
199
|
-
```
|
|
200
|
-
config.server_timing = true
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
Applications that expose timing only to authenticated diagnostics can set
|
|
204
|
-
`config.profile_token`. When this value is present, Crawlscope sends it as
|
|
205
|
-
`X-Profile-Token` on same-origin sitemap, HTTP, and browser document requests.
|
|
206
|
-
The header survives same-origin redirects but is removed before a cross-origin
|
|
207
|
-
redirect, and browser subresources never receive it. Keep the token in the host
|
|
208
|
-
application's secret store rather than a URL, command argument, report, or
|
|
209
|
-
checked-in configuration.
|
|
210
|
-
|
|
211
|
-
`CRAWLSCOPE_PROFILE_TOKEN` is the portable default for Rails, the standalone
|
|
212
|
-
CLI, Rake tasks, and plain Ruby callers. Rails applications may instead assign
|
|
213
|
-
`config.profile_token` from encrypted credentials when that is their established
|
|
214
|
-
secret-management boundary; explicit configuration takes precedence over the
|
|
215
|
-
environment.
|
|
216
|
-
|
|
217
|
-
New Rails applications enable it in development by default; production remains
|
|
218
|
-
opt-in. Rails publishes the Active Support notification names observed during
|
|
219
|
-
each request and sums repeated events with the same name. The exact metrics
|
|
220
|
-
therefore depend on the application and request, but commonly include controller,
|
|
221
|
-
view, database, and cache instrumentation. Crawlscope accepts Rails' dotted
|
|
222
|
-
metric names and reports each metric independently.
|
|
223
|
-
|
|
224
|
-
## Rails Usage
|
|
225
|
-
|
|
226
|
-
Install Rails 8 in the host application; Crawlscope does not install Railties.
|
|
227
|
-
For a process that loads Crawlscope before Rails, require `crawlscope/rails`
|
|
228
|
-
after Rails is available. The Ruby and CLI entry points remain independent of Rails.
|
|
229
|
-
|
|
230
|
-
Run the install generator after adding the gem:
|
|
231
|
-
|
|
232
|
-
```bash
|
|
181
|
+
```sh
|
|
233
182
|
bin/rails generate crawlscope:install
|
|
183
|
+
bin/rails crawlscope:validate
|
|
234
184
|
```
|
|
235
185
|
|
|
236
|
-
|
|
237
|
-
`
|
|
238
|
-
|
|
239
|
-
the
|
|
240
|
-
|
|
241
|
-
Customize the `Crawlscope.configure` block inside the generated initializer:
|
|
242
|
-
|
|
243
|
-
```ruby
|
|
244
|
-
Crawlscope.configure do |config|
|
|
245
|
-
config.base_url = -> { ENV.fetch("CRAWLSCOPE_BASE_URL", "http://localhost:3000") }
|
|
246
|
-
config.sitemap_path = lambda {
|
|
247
|
-
ENV.fetch("SITEMAP", "#{config.base_url.to_s.chomp("/")}/sitemap.xml")
|
|
248
|
-
}
|
|
249
|
-
config.site_name = ENV.fetch("CRAWLSCOPE_SITE_NAME", "Application")
|
|
250
|
-
end
|
|
251
|
-
```
|
|
252
|
-
|
|
253
|
-
Runtime callers must apply the generated configuration before using
|
|
254
|
-
Crawlscope:
|
|
186
|
+
Edit the generated `Crawlscope.configure` block to set `base_url`,
|
|
187
|
+
`sitemap_path` and any custom rule/schema registries. Tasks load and apply it
|
|
188
|
+
automatically. From runtime application code, apply the generated loader and
|
|
189
|
+
execute the crawl explicitly:
|
|
255
190
|
|
|
256
191
|
```ruby
|
|
257
192
|
CrawlscopeConfiguration.apply
|
|
258
|
-
Crawlscope.configuration.audit
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
Rake tasks apply it automatically because `crawlscope/tasks` loads the gem
|
|
262
|
-
before Rails evaluates the initializer. Each Rake entry point passes the shared
|
|
263
|
-
`Crawlscope.configuration` object to the CLI, so configured base URLs, sitemap
|
|
264
|
-
paths, registries, and runtime settings are preserved unless a task argument or
|
|
265
|
-
environment override replaces them.
|
|
266
|
-
|
|
267
|
-
Then run:
|
|
268
|
-
|
|
269
|
-
```bash
|
|
270
|
-
bin/rails crawlscope:validate
|
|
271
|
-
```
|
|
272
|
-
|
|
273
|
-
Available environment overrides:
|
|
274
|
-
|
|
275
|
-
- `URL`
|
|
276
|
-
- `SITEMAP`
|
|
277
|
-
- `CRAWLSCOPE_PROFILE_TOKEN`
|
|
278
|
-
- `RULES=metadata,links`
|
|
279
|
-
- `JS=1` or `RENDERER=browser`
|
|
280
|
-
- `TIMEOUT=30`
|
|
281
|
-
- `NETWORK_IDLE_TIMEOUT=10`
|
|
282
|
-
- `CONCURRENCY=5`
|
|
283
|
-
- `FETCH_EXECUTOR=threaded` or `FETCH_EXECUTOR=async`
|
|
284
|
-
|
|
285
|
-
Available tasks:
|
|
286
|
-
|
|
287
|
-
```bash
|
|
288
|
-
bin/rails crawlscope:validate
|
|
289
|
-
bin/rails crawlscope:validate:indexability
|
|
290
|
-
bin/rails crawlscope:validate:metadata
|
|
291
|
-
bin/rails crawlscope:validate:structured_data
|
|
292
|
-
bin/rails crawlscope:validate:uniqueness
|
|
293
|
-
bin/rails crawlscope:validate:content_quality
|
|
294
|
-
bin/rails crawlscope:validate:links
|
|
295
|
-
bin/rails crawlscope:validate:ldjson
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
The same validation surface is also available in the gem repository itself through plain `rake`:
|
|
299
|
-
|
|
300
|
-
```bash
|
|
301
|
-
bundle exec rake crawlscope:validate URL=https://example.com
|
|
302
|
-
bundle exec rake 'crawlscope:validate[https://example.com]'
|
|
303
|
-
bundle exec rake crawlscope:validate:metadata URL=https://example.com
|
|
304
|
-
bundle exec rake 'crawlscope:validate:metadata[https://example.com]'
|
|
305
|
-
bundle exec rake crawlscope:validate:ldjson URL=https://example.com/article
|
|
306
|
-
bundle exec rake 'crawlscope:validate:ldjson[https://example.com/article]'
|
|
307
|
-
```
|
|
308
|
-
|
|
309
|
-
`crawlscope:validate` runs all default sitemap rules: indexability, metadata,
|
|
310
|
-
structured data, uniqueness, content quality, and links. `URL` is the site
|
|
311
|
-
base. Without `SITEMAP`, Crawlscope uses the configured sitemap URL, then fetches
|
|
312
|
-
`/sitemap.xml` from `URL` over HTTP. With `SITEMAP`, Crawlscope uses `URL` as
|
|
313
|
-
the site base and validates URLs from that sitemap. `SITEMAP` may be a full URL
|
|
314
|
-
or an explicitly selected local file path.
|
|
315
|
-
|
|
316
|
-
Plain `rake` does not pass `--url` style flags to tasks. Use `URL=...` or the
|
|
317
|
-
task-argument form above instead.
|
|
318
|
-
|
|
319
|
-
`FETCH_EXECUTOR=async` is the default for HTTP crawling. It uses Ruby's fiber
|
|
320
|
-
scheduler and Async::HTTP through Faraday, preserving the same `CONCURRENCY`
|
|
321
|
-
bound. Use `FETCH_EXECUTOR=threaded` or `--fetch-executor threaded` for the
|
|
322
|
-
thread-pool executor. Browser rendering uses the threaded executor by default
|
|
323
|
-
because async fetch execution is only supported with HTTP rendering.
|
|
324
|
-
|
|
325
|
-
`crawlscope:validate:ldjson` is separate because it directly checks the URL or semicolon-separated URLs in `URL`; it does not crawl the sitemap. Without `URL`, it checks the configured base URL, falling back to `http://localhost:3000`.
|
|
326
|
-
|
|
327
|
-
### Structured Data URL Audit
|
|
328
|
-
|
|
329
|
-
For one-off structured-data checks:
|
|
330
|
-
|
|
331
|
-
```bash
|
|
332
|
-
bin/rails crawlscope:validate:ldjson URL=https://example.com/article
|
|
333
|
-
bin/rails crawlscope:validate:ldjson URL='https://example.com/a;https://example.com/b' SUMMARY=1
|
|
334
|
-
bin/rails crawlscope:validate:ldjson URL=https://example.com/article REPORT_PATH=tmp/structured-data.json
|
|
193
|
+
result = Crawlscope.configuration.audit.call
|
|
335
194
|
```
|
|
336
195
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
- `DEBUG=1`: print detected items
|
|
340
|
-
- `SUMMARY=1`: print grouped failures
|
|
341
|
-
- `REPORT_PATH=...`: write a JSON report. Treat this as trusted operator input; Crawlscope writes to the path the task process can access.
|
|
342
|
-
- `JS=1` or `RENDERER=browser`: render with Ferrum
|
|
343
|
-
|
|
344
|
-
## Rules
|
|
345
|
-
|
|
346
|
-
Built-in rules:
|
|
347
|
-
|
|
348
|
-
- `indexability`
|
|
349
|
-
- `metadata`
|
|
350
|
-
- `structured_data`
|
|
351
|
-
- `uniqueness`
|
|
352
|
-
- `content_quality`
|
|
353
|
-
- `links`
|
|
354
|
-
|
|
355
|
-
### Indexability
|
|
356
|
-
|
|
357
|
-
Checks:
|
|
358
|
-
|
|
359
|
-
- page-level meta robots `noindex`
|
|
360
|
-
- `X-Robots-Tag: noindex`
|
|
361
|
-
|
|
362
|
-
### Metadata
|
|
363
|
-
|
|
364
|
-
Checks:
|
|
365
|
-
|
|
366
|
-
- missing `<h1>`
|
|
367
|
-
- missing `<title>`
|
|
368
|
-
- title length
|
|
369
|
-
- repeated site name in the title
|
|
370
|
-
- missing meta description
|
|
371
|
-
- meta description length
|
|
372
|
-
- missing canonical link
|
|
373
|
-
- canonical mismatch
|
|
374
|
-
|
|
375
|
-
### Structured Data
|
|
196
|
+
See [Rails integration](docs/rails.md) for lazy loading, all tasks,
|
|
197
|
+
environment overrides and structured-data URL audits.
|
|
376
198
|
|
|
377
|
-
|
|
199
|
+
## Configuration and limits
|
|
378
200
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
201
|
+
The shared configuration defaults to HTTP rendering, the async fetch executor,
|
|
202
|
+
10 concurrent requests and a 20-second request timeout. Browser rendering uses
|
|
203
|
+
the threaded executor with at most four concurrent browser fetches. Configure
|
|
204
|
+
`concurrency`, `timeout_seconds`, `renderer`, `fetch_executor`, `rule_registry`
|
|
205
|
+
and `schema_registry` through `Crawlscope.configure` when using that shared
|
|
206
|
+
configuration; direct `Crawlscope::Crawl.new` callers pass their own options.
|
|
383
207
|
|
|
384
|
-
|
|
208
|
+
Use [rule and schema registries](docs/rules.md) to select or extend checks.
|
|
209
|
+
Near-duplicate scanning is limited to 250 HTML pages by default; exact duplicate
|
|
210
|
+
checks still run for larger crawls. Validation describes observed responses and
|
|
211
|
+
configured rules; it does not establish Google rich-result eligibility or
|
|
212
|
+
search ranking.
|
|
385
213
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
- duplicate titles
|
|
389
|
-
- duplicate meta descriptions
|
|
390
|
-
- duplicate content fingerprints
|
|
391
|
-
- near-duplicate visible content for up to 250 HTML pages
|
|
392
|
-
|
|
393
|
-
For larger crawls, exact duplicate checks still run and Crawlscope reports
|
|
394
|
-
`near_duplicate_scan_skipped`. Configure `Rules::Uniqueness` with
|
|
395
|
-
`max_near_duplicate_pages:` in a custom rule registry to change the limit.
|
|
396
|
-
|
|
397
|
-
### Content Quality
|
|
398
|
-
|
|
399
|
-
Checks:
|
|
400
|
-
|
|
401
|
-
- thin visible text
|
|
402
|
-
- low visible-text-to-HTML ratio
|
|
403
|
-
- low unique-token ratio
|
|
404
|
-
|
|
405
|
-
### Links
|
|
406
|
-
|
|
407
|
-
Checks:
|
|
408
|
-
|
|
409
|
-
- broken internal links
|
|
410
|
-
- unresolved internal links
|
|
411
|
-
- low inbound anchor-link counts
|
|
412
|
-
|
|
413
|
-
## Schema Registry
|
|
414
|
-
|
|
415
|
-
`crawlscope` ships with a default schema registry for common types such as:
|
|
416
|
-
|
|
417
|
-
- `Article`
|
|
418
|
-
- `FAQPage`
|
|
419
|
-
- `Organization`
|
|
420
|
-
- `Product`
|
|
421
|
-
- `Review`
|
|
422
|
-
- `SoftwareApplication`
|
|
423
|
-
- `WebApplication`
|
|
424
|
-
- `WebSite`
|
|
425
|
-
|
|
426
|
-
The default schema definitions live in `Crawlscope::Schemas`; `Crawlscope::SchemaRegistry` owns registration and validation.
|
|
427
|
-
|
|
428
|
-
Host apps can replace or extend the registry:
|
|
429
|
-
|
|
430
|
-
```ruby
|
|
431
|
-
Crawlscope.configure do |config|
|
|
432
|
-
config.schema_registry = -> { MyApp::StructuredData::SchemaRegistry.new }
|
|
433
|
-
end
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
That makes `crawlscope` useful as the audit engine while the app remains the owner of stricter product-specific schema rules.
|
|
214
|
+
[Server timing](docs/server-timing.md) covers response timings, diagnostic
|
|
215
|
+
profile tokens and the limits of those measurements.
|
|
437
216
|
|
|
438
217
|
## Contributing
|
|
439
218
|
|
|
@@ -463,3 +242,15 @@ bundle exec rake verify
|
|
|
463
242
|
Tests use controlled local fixtures and need no service account.
|
|
464
243
|
Release preparation, publication, registry setup and recovery are documented
|
|
465
244
|
in the [Gemkit release guide](https://github.com/ethos-link/ethos-gemkit#releasing-a-gem).
|
|
245
|
+
|
|
246
|
+
## About Ethos Link
|
|
247
|
+
|
|
248
|
+
[Ethos Link](https://www.ethos-link.com) is a software company founded by
|
|
249
|
+
[Paulo Fidalgo](https://www.fidalgo.pt). This gem is part of the open-source
|
|
250
|
+
tooling we publish alongside our business products:
|
|
251
|
+
|
|
252
|
+
- [Reviato](https://www.reviato.com): collect private customer feedback and analyse public reviews.
|
|
253
|
+
- [Nuntio](https://www.nuntiohq.com): prepare, review and schedule social posts.
|
|
254
|
+
- [EthosPlaces](https://www.ethos.place): find places, opening hours and contact details.
|
|
255
|
+
|
|
256
|
+
Explore our [open-source projects](https://www.ethos-link.com/opensource).
|
data/docs/rails.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# Rails integration
|
|
2
|
+
|
|
3
|
+
[README](../README.md) · [Project guide](https://www.ethos-link.com/opensource/crawlscope)
|
|
4
|
+
|
|
5
|
+
Install Rails 8 in the host application; Crawlscope does not install Railties.
|
|
6
|
+
For a process that loads Crawlscope before Rails, require `crawlscope/rails`
|
|
7
|
+
after Rails is available. The Ruby and CLI entry points remain independent of Rails.
|
|
8
|
+
|
|
9
|
+
Run the install generator after adding the gem:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
bin/rails generate crawlscope:install
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The generator creates `config/initializers/crawlscope.rb` with an idempotent
|
|
16
|
+
`CrawlscopeConfiguration.apply` loader and adds conditional `crawlscope/tasks`
|
|
17
|
+
loading to the application's `Rakefile`. This preserves `bin/rails --tasks` and
|
|
18
|
+
the `crawlscope:*` tasks without loading Crawlscope during normal Rails boot.
|
|
19
|
+
|
|
20
|
+
Customize the `Crawlscope.configure` block inside the generated initializer:
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
Crawlscope.configure do |config|
|
|
24
|
+
config.base_url = -> { ENV.fetch("CRAWLSCOPE_BASE_URL", "http://localhost:3000") }
|
|
25
|
+
config.sitemap_path = lambda {
|
|
26
|
+
ENV.fetch("SITEMAP", "#{config.base_url.to_s.chomp("/")}/sitemap.xml")
|
|
27
|
+
}
|
|
28
|
+
config.site_name = ENV.fetch("CRAWLSCOPE_SITE_NAME", "Application")
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Runtime callers must apply the generated configuration before using
|
|
33
|
+
Crawlscope:
|
|
34
|
+
|
|
35
|
+
```ruby
|
|
36
|
+
CrawlscopeConfiguration.apply
|
|
37
|
+
Crawlscope.configuration.audit.call
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Rake tasks apply it automatically because `crawlscope/tasks` loads the gem
|
|
41
|
+
before Rails evaluates the initializer. Each Rake entry point passes the shared
|
|
42
|
+
`Crawlscope.configuration` object to the CLI, so configured base URLs, sitemap
|
|
43
|
+
paths, registries, and runtime settings are preserved unless a task argument or
|
|
44
|
+
environment override replaces them.
|
|
45
|
+
|
|
46
|
+
Then run:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
bin/rails crawlscope:validate
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Available environment overrides:
|
|
53
|
+
|
|
54
|
+
- `URL`
|
|
55
|
+
- `SITEMAP`
|
|
56
|
+
- `CRAWLSCOPE_PROFILE_TOKEN`
|
|
57
|
+
- `RULES=metadata,links`
|
|
58
|
+
- `JS=1` or `RENDERER=browser`
|
|
59
|
+
- `TIMEOUT=30`
|
|
60
|
+
- `NETWORK_IDLE_TIMEOUT=10`
|
|
61
|
+
- `CONCURRENCY=5`
|
|
62
|
+
- `FETCH_EXECUTOR=threaded` or `FETCH_EXECUTOR=async`
|
|
63
|
+
|
|
64
|
+
Available tasks:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
bin/rails crawlscope:validate
|
|
68
|
+
bin/rails crawlscope:validate:indexability
|
|
69
|
+
bin/rails crawlscope:validate:metadata
|
|
70
|
+
bin/rails crawlscope:validate:structured_data
|
|
71
|
+
bin/rails crawlscope:validate:uniqueness
|
|
72
|
+
bin/rails crawlscope:validate:content_quality
|
|
73
|
+
bin/rails crawlscope:validate:links
|
|
74
|
+
bin/rails crawlscope:validate:ldjson
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
The same validation surface is also available in the gem repository itself through plain `rake`:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
bundle exec rake crawlscope:validate URL=https://example.com
|
|
81
|
+
bundle exec rake 'crawlscope:validate[https://example.com]'
|
|
82
|
+
bundle exec rake crawlscope:validate:metadata URL=https://example.com
|
|
83
|
+
bundle exec rake 'crawlscope:validate:metadata[https://example.com]'
|
|
84
|
+
bundle exec rake crawlscope:validate:ldjson URL=https://example.com/article
|
|
85
|
+
bundle exec rake 'crawlscope:validate:ldjson[https://example.com/article]'
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`crawlscope:validate` runs all default sitemap rules: indexability, metadata,
|
|
89
|
+
structured data, uniqueness, content quality, and links. `URL` is the site
|
|
90
|
+
base. Without `SITEMAP`, Crawlscope uses the configured sitemap URL, then fetches
|
|
91
|
+
`/sitemap.xml` from `URL` over HTTP. With `SITEMAP`, Crawlscope uses `URL` as
|
|
92
|
+
the site base and validates URLs from that sitemap. `SITEMAP` may be a full URL
|
|
93
|
+
or an explicitly selected local file path.
|
|
94
|
+
|
|
95
|
+
Plain `rake` does not pass `--url` style flags to tasks. Use `URL=...` or the
|
|
96
|
+
task-argument form above instead.
|
|
97
|
+
|
|
98
|
+
`FETCH_EXECUTOR=async` is the default for HTTP crawling. It uses Ruby's fiber
|
|
99
|
+
scheduler and Async::HTTP through Faraday, preserving the same `CONCURRENCY`
|
|
100
|
+
bound. Use `FETCH_EXECUTOR=threaded` or `--fetch-executor threaded` for the
|
|
101
|
+
thread-pool executor. Browser rendering uses the threaded executor by default
|
|
102
|
+
because async fetch execution is only supported with HTTP rendering.
|
|
103
|
+
|
|
104
|
+
`crawlscope:validate:ldjson` is separate because it directly checks the URL or semicolon-separated URLs in `URL`; it does not crawl the sitemap. Without `URL`, it checks the configured base URL, falling back to `http://localhost:3000`.
|
|
105
|
+
|
|
106
|
+
## Structured Data URL Audit
|
|
107
|
+
|
|
108
|
+
For one-off structured-data checks:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
bin/rails crawlscope:validate:ldjson URL=https://example.com/article
|
|
112
|
+
bin/rails crawlscope:validate:ldjson URL='https://example.com/a;https://example.com/b' SUMMARY=1
|
|
113
|
+
bin/rails crawlscope:validate:ldjson URL=https://example.com/article REPORT_PATH=tmp/structured-data.json
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Optional flags:
|
|
117
|
+
|
|
118
|
+
- `DEBUG=1`: print detected items
|
|
119
|
+
- `SUMMARY=1`: print grouped failures
|
|
120
|
+
- `REPORT_PATH=...`: write a JSON report. Treat this as trusted operator input; Crawlscope writes to the path the task process can access.
|
|
121
|
+
- `JS=1` or `RENDERER=browser`: render with Ferrum
|
data/docs/rules.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Rules and schemas
|
|
2
|
+
|
|
3
|
+
[README](../README.md) · [Project guide](https://www.ethos-link.com/opensource/crawlscope)
|
|
4
|
+
|
|
5
|
+
Built-in rules:
|
|
6
|
+
|
|
7
|
+
- `indexability`
|
|
8
|
+
- `metadata`
|
|
9
|
+
- `structured_data`
|
|
10
|
+
- `uniqueness`
|
|
11
|
+
- `content_quality`
|
|
12
|
+
- `links`
|
|
13
|
+
|
|
14
|
+
## Indexability
|
|
15
|
+
|
|
16
|
+
Checks:
|
|
17
|
+
|
|
18
|
+
- page-level meta robots `noindex`
|
|
19
|
+
- `X-Robots-Tag: noindex`
|
|
20
|
+
|
|
21
|
+
## Metadata
|
|
22
|
+
|
|
23
|
+
Checks:
|
|
24
|
+
|
|
25
|
+
- missing `<h1>`
|
|
26
|
+
- missing `<title>`
|
|
27
|
+
- title length
|
|
28
|
+
- repeated site name in the title
|
|
29
|
+
- missing meta description
|
|
30
|
+
- meta description length
|
|
31
|
+
- missing canonical link
|
|
32
|
+
- canonical mismatch
|
|
33
|
+
|
|
34
|
+
## Structured Data
|
|
35
|
+
|
|
36
|
+
Checks:
|
|
37
|
+
|
|
38
|
+
- malformed JSON-LD
|
|
39
|
+
- missing required fields for supported schema types
|
|
40
|
+
- schema validation failures from the configured registry
|
|
41
|
+
- direct URL structured-data audits through `crawlscope:validate:ldjson`
|
|
42
|
+
|
|
43
|
+
## Uniqueness
|
|
44
|
+
|
|
45
|
+
Checks:
|
|
46
|
+
|
|
47
|
+
- duplicate titles
|
|
48
|
+
- duplicate meta descriptions
|
|
49
|
+
- duplicate content fingerprints
|
|
50
|
+
- near-duplicate visible content for up to 250 HTML pages
|
|
51
|
+
|
|
52
|
+
For larger crawls, exact duplicate checks still run and Crawlscope reports
|
|
53
|
+
`near_duplicate_scan_skipped`. Configure `Rules::Uniqueness` with
|
|
54
|
+
`max_near_duplicate_pages:` in a custom rule registry to change the limit.
|
|
55
|
+
|
|
56
|
+
## Content Quality
|
|
57
|
+
|
|
58
|
+
Checks:
|
|
59
|
+
|
|
60
|
+
- thin visible text
|
|
61
|
+
- low visible-text-to-HTML ratio
|
|
62
|
+
- low unique-token ratio
|
|
63
|
+
|
|
64
|
+
## Links
|
|
65
|
+
|
|
66
|
+
Checks:
|
|
67
|
+
|
|
68
|
+
- broken internal links
|
|
69
|
+
- unresolved internal links
|
|
70
|
+
- low inbound anchor-link counts
|
|
71
|
+
|
|
72
|
+
## Schema Registry
|
|
73
|
+
|
|
74
|
+
`crawlscope` ships with a default schema registry for common types such as:
|
|
75
|
+
|
|
76
|
+
- `Article`
|
|
77
|
+
- `FAQPage`
|
|
78
|
+
- `Organization`
|
|
79
|
+
- `Product`
|
|
80
|
+
- `Review`
|
|
81
|
+
- `SoftwareApplication`
|
|
82
|
+
- `WebApplication`
|
|
83
|
+
- `WebSite`
|
|
84
|
+
|
|
85
|
+
The default schema definitions live in `Crawlscope::Schemas`; `Crawlscope::SchemaRegistry` owns registration and validation.
|
|
86
|
+
|
|
87
|
+
Host apps can replace or extend the registry:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
Crawlscope.configure do |config|
|
|
91
|
+
config.schema_registry = -> { MyApp::StructuredData::SchemaRegistry.new }
|
|
92
|
+
end
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
That makes `crawlscope` useful as the audit engine while the app remains the owner of stricter product-specific schema rules.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Server timing
|
|
2
|
+
|
|
3
|
+
[README](../README.md) · [Project guide](https://www.ethos-link.com/opensource/crawlscope)
|
|
4
|
+
|
|
5
|
+
Crawlscope parses the
|
|
6
|
+
[`Server-Timing`](https://www.w3.org/TR/server-timing/) response header for both
|
|
7
|
+
HTTP and browser-rendered crawls. Each page exposes its parsed header through
|
|
8
|
+
`page.server_timing`:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
page.server_timing.each do |metric|
|
|
12
|
+
puts [metric.name, metric.duration, metric.description].compact.join(": ")
|
|
13
|
+
end
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The validation report adds a `Server Timing` section only when at least one page
|
|
17
|
+
publishes the header. It includes:
|
|
18
|
+
|
|
19
|
+
- header coverage and the number of pages publishing durations
|
|
20
|
+
- sample and page counts with average, p50, p95, and maximum per metric
|
|
21
|
+
- non-duration signals, including cache status and routing descriptions
|
|
22
|
+
- the ten pages with the largest individual metric
|
|
23
|
+
- the number of malformed entries ignored during parsing
|
|
24
|
+
|
|
25
|
+
`dur` values are reported as milliseconds, as recommended by the specification.
|
|
26
|
+
Crawlscope does not add durations together because metrics such as `total`,
|
|
27
|
+
`app`, and `db` may overlap. A page's worst-offender entry is its largest
|
|
28
|
+
individual metric instead.
|
|
29
|
+
|
|
30
|
+
The current HTTP and browser transports expose response headers, not response
|
|
31
|
+
trailers. Metrics published only in a `Server-Timing` trailer are therefore not
|
|
32
|
+
available to Crawlscope.
|
|
33
|
+
|
|
34
|
+
Rails 8 applications can enable `ActionDispatch::ServerTiming` with:
|
|
35
|
+
|
|
36
|
+
```ruby
|
|
37
|
+
config.server_timing = true
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Applications that expose timing only to authenticated diagnostics can set
|
|
41
|
+
`config.profile_token`. When this value is present, Crawlscope sends it as
|
|
42
|
+
`X-Profile-Token` on same-origin sitemap, HTTP, and browser document requests.
|
|
43
|
+
The header survives same-origin redirects but is removed before a cross-origin
|
|
44
|
+
redirect, and browser subresources never receive it. Keep the token in the host
|
|
45
|
+
application's secret store rather than a URL, command argument, report, or
|
|
46
|
+
checked-in configuration.
|
|
47
|
+
|
|
48
|
+
`CRAWLSCOPE_PROFILE_TOKEN` is the portable default for Rails, the standalone
|
|
49
|
+
CLI, Rake tasks, and plain Ruby callers. Rails applications may instead assign
|
|
50
|
+
`config.profile_token` from encrypted credentials when that is their established
|
|
51
|
+
secret-management boundary; explicit configuration takes precedence over the
|
|
52
|
+
environment.
|
|
53
|
+
|
|
54
|
+
New Rails applications enable Server-Timing in development by default;
|
|
55
|
+
production remains opt-in. Rails publishes the Active Support notification names observed during
|
|
56
|
+
each request and sums repeated events with the same name. The exact metrics
|
|
57
|
+
therefore depend on the application and request, but commonly include controller,
|
|
58
|
+
view, database, and cache instrumentation. Crawlscope accepts Rails' dotted
|
|
59
|
+
metric names and reports each metric independently.
|
data/lib/crawlscope/version.rb
CHANGED
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.10.
|
|
4
|
+
version: 0.10.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Paulo Fidalgo
|
|
@@ -178,6 +178,9 @@ files:
|
|
|
178
178
|
- LICENSE.txt
|
|
179
179
|
- README.md
|
|
180
180
|
- UPGRADE.md
|
|
181
|
+
- docs/rails.md
|
|
182
|
+
- docs/rules.md
|
|
183
|
+
- docs/server-timing.md
|
|
181
184
|
- exe/crawlscope
|
|
182
185
|
- lib/crawlscope.rb
|
|
183
186
|
- lib/crawlscope/browser.rb
|
|
@@ -270,7 +273,7 @@ metadata:
|
|
|
270
273
|
source_code_uri: https://github.com/ethos-link/crawlscope
|
|
271
274
|
bug_tracker_uri: https://github.com/ethos-link/crawlscope/issues
|
|
272
275
|
changelog_uri: https://github.com/ethos-link/crawlscope/blob/main/CHANGELOG.md
|
|
273
|
-
documentation_uri: https://
|
|
276
|
+
documentation_uri: https://www.ethos-link.com/opensource/crawlscope
|
|
274
277
|
funding_uri: https://www.reviato.com/
|
|
275
278
|
github_repo: ssh://github.com/ethos-link/crawlscope
|
|
276
279
|
allowed_push_host: https://rubygems.org
|