bidi2pdf 0.1.13 → 0.1.15
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/.devcontainer/Dockerfile +53 -0
- data/.devcontainer/devcontainer.json +51 -0
- data/.devcontainer/docker-compose.yml +33 -0
- data/.ruby-gemset +1 -1
- data/.ruby-version +1 -1
- data/CHANGELOG.md +93 -6
- data/README.md +80 -5
- data/docker/Dockerfile +1 -1
- data/docker/Dockerfile.slim +2 -2
- data/docker/chrome-driver/chrome.json +874 -0
- data/lib/bidi2pdf/bidi/browser_tab.rb +97 -4
- data/lib/bidi2pdf/bidi/buffered_web_socket_client.rb +178 -0
- data/lib/bidi2pdf/bidi/client.rb +12 -5
- data/lib/bidi2pdf/bidi/commands/base.rb +2 -0
- data/lib/bidi2pdf/bidi/commands/capture_screenshot.rb +33 -0
- data/lib/bidi2pdf/bidi/commands/set_viewport.rb +32 -0
- data/lib/bidi2pdf/bidi/commands.rb +2 -0
- data/lib/bidi2pdf/bidi/network_event.rb +12 -3
- data/lib/bidi2pdf/bidi/network_events.rb +9 -1
- data/lib/bidi2pdf/bidi/session.rb +1 -1
- data/lib/bidi2pdf/chromedriver_manager.rb +10 -6
- data/lib/bidi2pdf/cli.rb +11 -1
- data/lib/bidi2pdf/launcher.rb +5 -3
- data/lib/bidi2pdf/notifications/logging_subscriber.rb +2 -0
- data/lib/bidi2pdf/session_warmer.rb +377 -0
- data/lib/bidi2pdf/test_helpers/images.rb +8 -0
- data/lib/bidi2pdf/test_helpers/pdf_reader_utils.rb +1 -2
- data/lib/bidi2pdf/test_helpers/pdf_text_sanitizer.rb +1 -2
- data/lib/bidi2pdf/test_helpers/testcontainers/chromedriver_container.rb +1 -1
- data/lib/bidi2pdf/test_helpers/testcontainers/chromedriver_test_helper.rb +22 -2
- data/lib/bidi2pdf/test_helpers/testcontainers/container_endpoint.rb +30 -0
- data/lib/bidi2pdf/test_helpers/testcontainers/testcontainers_refinement.rb +33 -0
- data/lib/bidi2pdf/test_helpers/testcontainers.rb +1 -0
- data/lib/bidi2pdf/version.rb +1 -1
- data/lib/bidi2pdf.rb +26 -1
- data/sig/bidi2pdf/bidi/add_headers_interceptor.rbs +16 -9
- data/sig/bidi2pdf/bidi/auth_interceptor.rbs +32 -7
- data/sig/bidi2pdf/bidi/browser.rbs +4 -31
- data/sig/bidi2pdf/bidi/browser_console_logger.rbs +59 -0
- data/sig/bidi2pdf/bidi/browser_tab.rbs +269 -25
- data/sig/bidi2pdf/bidi/buffered_web_socket_client.rbs +81 -0
- data/sig/bidi2pdf/bidi/client.rbs +103 -43
- data/sig/bidi2pdf/bidi/command_manager.rbs +5 -17
- data/sig/bidi2pdf/bidi/commands/add_intercept.rbs +16 -8
- data/sig/bidi2pdf/bidi/commands/base.rbs +49 -9
- data/sig/bidi2pdf/bidi/commands/browser_close.rbs +3 -1
- data/sig/bidi2pdf/bidi/commands/browser_create_user_context.rbs +2 -0
- data/sig/bidi2pdf/bidi/commands/browser_remove_user_context.rbs +19 -0
- data/sig/bidi2pdf/bidi/commands/browsing_context_close.rbs +8 -2
- data/sig/bidi2pdf/bidi/commands/browsing_context_navigate.rbs +12 -6
- data/sig/bidi2pdf/bidi/commands/browsing_context_print.rbs +10 -5
- data/sig/bidi2pdf/bidi/commands/cancel_auth.rbs +8 -2
- data/sig/bidi2pdf/bidi/commands/capture_screenshot.rbs +31 -0
- data/sig/bidi2pdf/bidi/commands/cdp_get_session.rbs +17 -0
- data/sig/bidi2pdf/bidi/commands/create_window.rbs +4 -0
- data/sig/bidi2pdf/bidi/commands/get_user_contexts.rbs +2 -0
- data/sig/bidi2pdf/bidi/commands/network_continue.rbs +4 -0
- data/sig/bidi2pdf/bidi/commands/page_print.rbs +28 -0
- data/sig/bidi2pdf/bidi/commands/print_parameters_validator.rbs +3 -0
- data/sig/bidi2pdf/bidi/commands/provide_credentials.rbs +12 -6
- data/sig/bidi2pdf/bidi/commands/script_evaluate.rbs +4 -0
- data/sig/bidi2pdf/bidi/commands/session_end.rbs +2 -0
- data/sig/bidi2pdf/bidi/commands/session_status.rbs +3 -1
- data/sig/bidi2pdf/bidi/commands/session_subscribe.rbs +4 -0
- data/sig/bidi2pdf/bidi/commands/set_tab_cookie.rbs +51 -23
- data/sig/bidi2pdf/bidi/commands/set_usercontext_cookie.rbs +17 -19
- data/sig/bidi2pdf/bidi/commands/set_viewport.rbs +31 -0
- data/sig/bidi2pdf/bidi/connection_manager.rbs +2 -1
- data/sig/bidi2pdf/bidi/event_manager.rbs +14 -19
- data/sig/bidi2pdf/bidi/interceptor.rbs +5 -0
- data/sig/bidi2pdf/bidi/js_logger_helper.rbs +9 -0
- data/sig/bidi2pdf/bidi/logger_events.rbs +21 -0
- data/sig/bidi2pdf/bidi/navigation_failed_events.rbs +17 -0
- data/sig/bidi2pdf/bidi/network_event.rbs +64 -46
- data/sig/bidi2pdf/bidi/network_event_formatters/network_event_console_formatter.rbs +63 -0
- data/sig/bidi2pdf/bidi/network_event_formatters/network_event_formatter_utils.rbs +15 -0
- data/sig/bidi2pdf/bidi/network_event_formatters/network_event_html_formatter.rbs +18 -0
- data/sig/bidi2pdf/bidi/network_event_formatters.rbs +6 -0
- data/sig/bidi2pdf/bidi/network_events.rbs +14 -39
- data/sig/bidi2pdf/bidi/session.rbs +144 -31
- data/sig/bidi2pdf/bidi/user_context.rbs +64 -45
- data/sig/bidi2pdf/bidi/web_socket_dispatcher.rbs +22 -30
- data/sig/bidi2pdf/chromedriver_manager.rbs +61 -22
- data/sig/bidi2pdf/cli.rbs +22 -2
- data/sig/bidi2pdf/dsl.rbs +34 -0
- data/sig/bidi2pdf/launcher.rbs +39 -1
- data/sig/bidi2pdf/notifications/event.rbs +40 -0
- data/sig/bidi2pdf/notifications/instrumenter.rbs +21 -0
- data/sig/bidi2pdf/notifications/logging_subscriber.rbs +41 -0
- data/sig/bidi2pdf/notifications.rbs +24 -0
- data/sig/bidi2pdf/process_tree.rbs +2 -2
- data/sig/bidi2pdf/session_runner.rbs +41 -1
- data/sig/bidi2pdf/session_warmer.rbs +221 -0
- data/sig/bidi2pdf/test_helpers/configuration.rbs +51 -0
- data/sig/bidi2pdf/test_helpers/images/extractor.rbs +43 -0
- data/sig/bidi2pdf/test_helpers/images/image_similarity_checker.rbs +35 -0
- data/sig/bidi2pdf/test_helpers/images/tiff_helper.rbs +70 -0
- data/sig/bidi2pdf/test_helpers/images.rbs +0 -0
- data/sig/bidi2pdf/test_helpers/matchers/contains_pdf_image.rbs +0 -0
- data/sig/bidi2pdf/test_helpers/matchers/contains_pdf_text.rbs +0 -0
- data/sig/bidi2pdf/test_helpers/matchers/have_pdf_page_count.rbs +0 -0
- data/sig/bidi2pdf/test_helpers/matchers/match_pdf_text.rbs +0 -0
- data/sig/bidi2pdf/test_helpers/pdf_file_helper.rbs +23 -0
- data/sig/bidi2pdf/test_helpers/pdf_reader_utils.rbs +49 -0
- data/sig/bidi2pdf/test_helpers/pdf_text_sanitizer.rbs +38 -0
- data/sig/bidi2pdf/test_helpers/spec_paths_helper.rbs +35 -0
- data/sig/bidi2pdf/test_helpers/testcontainers/chromedriver_container.rbs +43 -0
- data/sig/bidi2pdf/test_helpers/testcontainers/chromedriver_test_helper.rbs +45 -0
- data/sig/bidi2pdf/test_helpers/testcontainers/container_endpoint.rbs +25 -0
- data/sig/bidi2pdf/test_helpers/testcontainers/shared_docker_network.rbs +3 -0
- data/sig/bidi2pdf/test_helpers/testcontainers/testcontainers_refinement.rbs +47 -0
- data/sig/bidi2pdf/test_helpers/testcontainers.rbs +6 -0
- data/sig/bidi2pdf/test_helpers.rbs +0 -0
- data/sig/bidi2pdf/verbose_logger.rbs +37 -0
- data/sig/bidi2pdf/version.rbs +3 -0
- data/sig/bidi2pdf.rbs +130 -2
- data/tasks/generate_rbs.rake +11 -11
- data/tasks/release_credentials_check.rake +97 -0
- metadata +67 -10
- data/sig/bidi2pdf/utils.rbs +0 -5
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Bidi2pdf
|
|
2
|
+
module TestHelpers
|
|
3
|
+
# rubocop: disable-next Metrics/ModuleLength
|
|
4
|
+
module PDFTextSanitizer
|
|
5
|
+
def self.clean: (untyped text) -> untyped
|
|
6
|
+
|
|
7
|
+
def self.clean_pages: (untyped pdf) -> untyped
|
|
8
|
+
|
|
9
|
+
def self.normalize: (untyped text) -> untyped
|
|
10
|
+
|
|
11
|
+
def self.contains?: (untyped actual_pdf_thingy, untyped expected, ?untyped? page_number) -> (false | untyped)
|
|
12
|
+
|
|
13
|
+
def self.match_expected?: (untyped text, untyped expected) -> (false | untyped)
|
|
14
|
+
|
|
15
|
+
def self.match?: (untyped actual_pdf_thingy, untyped expected_pdf_thingy) -> (true | false)
|
|
16
|
+
|
|
17
|
+
def self.report_content_mismatch: (untyped actual, untyped expected) -> untyped
|
|
18
|
+
|
|
19
|
+
def self.content_mismatch_message: (untyped actual, untyped expected) -> untyped
|
|
20
|
+
|
|
21
|
+
def self.page_difference_message: (untyped actual_page, untyped expected_page, untyped page_idx) -> (nil | untyped)
|
|
22
|
+
|
|
23
|
+
def self.format_diff_output: (untyped diffs, untyped expected, untyped actual) -> untyped
|
|
24
|
+
|
|
25
|
+
def self.reporter: () -> untyped
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def self.group_changed_diffs: (untyped diffs) -> untyped
|
|
30
|
+
|
|
31
|
+
def self.format_change: (untyped expected, untyped actual, untyped change) -> untyped
|
|
32
|
+
|
|
33
|
+
def self.extract_snippets: (untyped expected, untyped actual, untyped change, untyped pos) -> { context_start: untyped, context: untyped, expected_snip: untyped, actual_snip: untyped }
|
|
34
|
+
|
|
35
|
+
def self.build_output: (untyped snip_data, untyped pos) -> ::Array[::String]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# This module provides helper methods for managing paths in test environments.
|
|
2
|
+
# It includes methods to retrieve directories and generate temporary file paths.
|
|
3
|
+
module Bidi2pdf
|
|
4
|
+
module TestHelpers
|
|
5
|
+
# This submodule contains path-related helper methods and configuration for tests.
|
|
6
|
+
module SpecPathsHelper
|
|
7
|
+
# Retrieves the directory path for Docker files.
|
|
8
|
+
# @return [String] the Docker directory path
|
|
9
|
+
def fixture_dir: () -> untyped
|
|
10
|
+
|
|
11
|
+
# Retrieves the directory path for fixtures.
|
|
12
|
+
# @return [String] the fixture directory path
|
|
13
|
+
def fixture_file: (*untyped) -> untyped
|
|
14
|
+
|
|
15
|
+
# Retrieves the directory path for specs.
|
|
16
|
+
# @return [String] the spec directory path
|
|
17
|
+
def spec_dir: () -> untyped
|
|
18
|
+
|
|
19
|
+
# Retrieves the directory path for temporary files.
|
|
20
|
+
# @return [String] the temporary directory path
|
|
21
|
+
def tmp_dir: () -> untyped
|
|
22
|
+
|
|
23
|
+
# Generates a path for a temporary file by joining the temporary directory with the given parts.
|
|
24
|
+
# @param [Array<String>] parts the parts of the file path to join
|
|
25
|
+
# @return [String] the full path to the temporary file
|
|
26
|
+
def tmp_file: (*untyped parts) -> untyped
|
|
27
|
+
|
|
28
|
+
# Generates a random temporary directory path.
|
|
29
|
+
# @param [Array<String>] dirs additional directory components to include in the path
|
|
30
|
+
# @param [String, nil] prefix an optional prefix for the directory name
|
|
31
|
+
# @return [String] the full path to the random temporary directory
|
|
32
|
+
def random_tmp_dir: (*untyped dirs, ?prefix: untyped?) -> untyped
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Bidi2pdf
|
|
2
|
+
module TestHelpers
|
|
3
|
+
module Testcontainers
|
|
4
|
+
class ChromedriverContainer < ::Testcontainers::DockerContainer
|
|
5
|
+
@docker_file: untyped
|
|
6
|
+
|
|
7
|
+
@build_dir: untyped
|
|
8
|
+
|
|
9
|
+
@wait_for: untyped
|
|
10
|
+
|
|
11
|
+
@_container: untyped
|
|
12
|
+
|
|
13
|
+
@_id: untyped
|
|
14
|
+
|
|
15
|
+
@name: untyped
|
|
16
|
+
|
|
17
|
+
@_created_at: untyped
|
|
18
|
+
|
|
19
|
+
DEFAULT_CHROMEDRIVER_PORT: 3000
|
|
20
|
+
|
|
21
|
+
DEFAULT_IMAGE: "dieters877565/chromedriver"
|
|
22
|
+
|
|
23
|
+
attr_reader docker_file: untyped
|
|
24
|
+
|
|
25
|
+
attr_reader build_dir: untyped
|
|
26
|
+
|
|
27
|
+
def initialize: (?untyped image, **untyped options) -> void
|
|
28
|
+
|
|
29
|
+
def start: () -> untyped
|
|
30
|
+
|
|
31
|
+
def port: () -> untyped
|
|
32
|
+
|
|
33
|
+
# rubocop: disable Metrics/AbcSize
|
|
34
|
+
def build_local_image: () -> untyped
|
|
35
|
+
|
|
36
|
+
# rubocop: disable Metrics/AbcSize
|
|
37
|
+
def start_local_image: () -> untyped
|
|
38
|
+
|
|
39
|
+
def session_url: (?protocol: ::String) -> untyped
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Bidi2pdf
|
|
2
|
+
module TestHelpers
|
|
3
|
+
module Testcontainers
|
|
4
|
+
module ChromedriverTestHelper
|
|
5
|
+
def session_url: () -> untyped
|
|
6
|
+
|
|
7
|
+
def chromedriver_container: () -> untyped
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module SessionTestHelper
|
|
11
|
+
def reporter: () -> untyped
|
|
12
|
+
|
|
13
|
+
def chrome_args: () -> untyped
|
|
14
|
+
|
|
15
|
+
def create_session: (untyped session_url) -> untyped
|
|
16
|
+
|
|
17
|
+
# Full session/tab lifecycle in one call - create_session above only builds the session
|
|
18
|
+
# object, it never starts or closes it, and there is no after(:each) teardown for a spec
|
|
19
|
+
# that uses it directly. Mirrors Bidi2pdf::DSL.with_tab's own ensure-based teardown, but
|
|
20
|
+
# against a session_url this module already knows about (a long-lived remote-chrome
|
|
21
|
+
# reached over `session:` metadata, not a freshly spawned local chromedriver per call).
|
|
22
|
+
def with_tab: (untyped session_url) { (untyped) -> untyped } -> untyped
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# alias the long class name
|
|
29
|
+
ChromedriverTestcontainer: untyped
|
|
30
|
+
|
|
31
|
+
class Object
|
|
32
|
+
def stop_container: (untyped container) -> (nil | untyped)
|
|
33
|
+
|
|
34
|
+
def dump_container_logs: (untyped container) -> (nil | untyped)
|
|
35
|
+
|
|
36
|
+
def stop_running_container: (untyped container) -> (nil | untyped)
|
|
37
|
+
|
|
38
|
+
def reporter: () -> untyped
|
|
39
|
+
|
|
40
|
+
def chromedriver_tests_present?: () -> untyped
|
|
41
|
+
|
|
42
|
+
def test_of_kind_present?: (untyped type) -> untyped
|
|
43
|
+
|
|
44
|
+
def start_chromedriver_container: (build_dir: untyped, mounts: untyped, shared_network: untyped, ?chromedriver_log_level: ::String) -> untyped
|
|
45
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Bidi2pdf
|
|
2
|
+
module TestHelpers
|
|
3
|
+
module Testcontainers
|
|
4
|
+
# Builds a URL for reaching a container's published port from the test
|
|
5
|
+
# process, using the container's own host/port resolution.
|
|
6
|
+
class ContainerEndpoint
|
|
7
|
+
@container: untyped
|
|
8
|
+
|
|
9
|
+
@port: untyped
|
|
10
|
+
|
|
11
|
+
def initialize: (untyped container, untyped port) -> void
|
|
12
|
+
|
|
13
|
+
def url: (?scheme: ::String, ?path: ::String) -> ::String
|
|
14
|
+
|
|
15
|
+
# Address reachable from another container on the same Docker network (e.g. remote-chrome
|
|
16
|
+
# reaching nginx), by alias and internal port - as opposed to #url's host-accessible,
|
|
17
|
+
# externally-mapped port. Uses the same @port this instance was built with, not a
|
|
18
|
+
# container_ports.first guess: the two methods must agree on which port they're both
|
|
19
|
+
# describing for the same container, or a caller comparing #url and #alias_url for what's
|
|
20
|
+
# meant to be the same endpoint could silently get two different services.
|
|
21
|
+
def alias_url: (?scheme: ::String, ?path: ::String) -> ::String
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Bidi2pdf
|
|
2
|
+
module TestHelpers
|
|
3
|
+
module TestcontainersRefinement
|
|
4
|
+
@aliases: untyped
|
|
5
|
+
|
|
6
|
+
@_network: untyped
|
|
7
|
+
|
|
8
|
+
def id: () -> untyped
|
|
9
|
+
|
|
10
|
+
def aliases: () -> untyped
|
|
11
|
+
|
|
12
|
+
def aliases=: (untyped aliases) -> untyped
|
|
13
|
+
|
|
14
|
+
def network: () -> untyped
|
|
15
|
+
|
|
16
|
+
def with_network: (untyped network) -> self
|
|
17
|
+
|
|
18
|
+
def with_network_aliases: (*untyped aliases) -> self
|
|
19
|
+
|
|
20
|
+
def container_json: () -> untyped
|
|
21
|
+
|
|
22
|
+
# Address of the Docker host at which this container's *mapped* ports
|
|
23
|
+
# are reachable from the test process.
|
|
24
|
+
#
|
|
25
|
+
# Resolution belongs to testcontainers: it honours TC_HOST, a tcp:/ssh:
|
|
26
|
+
# DOCKER_HOST, a native local daemon, and a sibling container reaching
|
|
27
|
+
# the daemon over the bridge gateway — and it derives #mapped_port from
|
|
28
|
+
# the same decision, so the two must stay on one path. We only add a
|
|
29
|
+
# fallback for the cases 0.2.0 leaves unresolved.
|
|
30
|
+
#
|
|
31
|
+
# This is not the address containers use to reach each other; for that
|
|
32
|
+
# the helpers join a shared network and address containers by alias.
|
|
33
|
+
def accessible_host: () -> untyped
|
|
34
|
+
|
|
35
|
+
# testcontainers-core 0.2.0 cannot resolve a host when the test process
|
|
36
|
+
# runs inside a container and the container under test is on a custom
|
|
37
|
+
# network: #container_gateway_ip hardcodes the "bridge" network key
|
|
38
|
+
# (docker_container.rb:1074), so #host returns nil. On the default
|
|
39
|
+
# bridge it can instead reach docker_container.rb:703, which calls an
|
|
40
|
+
# undefined `bridge_ip`. Both are fixed on testcontainers-ruby main;
|
|
41
|
+
# treat either as "unresolved" and fall back.
|
|
42
|
+
def resolved_host: () -> untyped
|
|
43
|
+
|
|
44
|
+
def _container_create_options: () -> untyped
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
File without changes
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Bidi2pdf
|
|
2
|
+
class VerboseLogger < SimpleDelegator
|
|
3
|
+
@logger: untyped
|
|
4
|
+
|
|
5
|
+
@verbosity: untyped
|
|
6
|
+
|
|
7
|
+
VERBOSITY_LEVELS: { none: 0, low: 1, medium: 2, high: 3 }
|
|
8
|
+
|
|
9
|
+
attr_reader logger: untyped
|
|
10
|
+
|
|
11
|
+
attr_reader verbosity: untyped
|
|
12
|
+
|
|
13
|
+
def initialize: (untyped logger, ?::Symbol verbosity) -> void
|
|
14
|
+
|
|
15
|
+
def verbosity=: (untyped verbosity) -> untyped
|
|
16
|
+
|
|
17
|
+
def verbosity_sym: () -> untyped
|
|
18
|
+
|
|
19
|
+
def debug1: (?untyped? progname) { (?) -> untyped } -> (nil | untyped)
|
|
20
|
+
|
|
21
|
+
def debug1?: () -> untyped
|
|
22
|
+
|
|
23
|
+
def debug1!: () -> untyped
|
|
24
|
+
|
|
25
|
+
def debug2: (?untyped? progname) { (?) -> untyped } -> (nil | untyped)
|
|
26
|
+
|
|
27
|
+
def debug2?: () -> untyped
|
|
28
|
+
|
|
29
|
+
def debug2!: () -> untyped
|
|
30
|
+
|
|
31
|
+
def debug3: (?untyped? progname) { (?) -> untyped } -> (nil | untyped)
|
|
32
|
+
|
|
33
|
+
def debug3?: () -> untyped
|
|
34
|
+
|
|
35
|
+
def debug3!: () -> untyped
|
|
36
|
+
end
|
|
37
|
+
end
|
data/sig/bidi2pdf.rbs
CHANGED
|
@@ -1,4 +1,132 @@
|
|
|
1
1
|
module Bidi2pdf
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
self.@logger: untyped
|
|
3
|
+
|
|
4
|
+
self.@network_events_logger: untyped
|
|
5
|
+
|
|
6
|
+
self.@browser_console_logger: untyped
|
|
7
|
+
|
|
8
|
+
self.@logging_subscriber: untyped
|
|
9
|
+
|
|
10
|
+
self.@notification_service: untyped
|
|
11
|
+
|
|
12
|
+
PAPER_FORMATS_CM: { letter: { width: ::Float, height: ::Float }, legal: { width: ::Float, height: ::Float }, tabloid: { width: ::Float, height: ::Float }, ledger: { width: ::Float, height: ::Float }, a0: { width: ::Float, height: ::Float }, a1: { width: ::Float, height: ::Float }, a2: { width: ::Float, height: ::Float }, a3: { width: ::Float, height: ::Float }, a4: { width: ::Float, height: ::Float }, a5: { width: ::Float, height: ::Float }, a6: { width: ::Float, height: ::Float } }
|
|
13
|
+
|
|
14
|
+
class Error < StandardError
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class SessionNotStartedError < Error
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class WebsocketError < Error
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class ClientError < WebsocketError
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class CmdError < ClientError
|
|
27
|
+
@cmd: untyped
|
|
28
|
+
|
|
29
|
+
@response: untyped
|
|
30
|
+
|
|
31
|
+
attr_reader cmd: untyped
|
|
32
|
+
|
|
33
|
+
attr_reader response: untyped
|
|
34
|
+
|
|
35
|
+
def initialize: (untyped cmd, untyped response) -> void
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class CmdResponseNotStoredError < ClientError
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
class CmdTimeoutError < ClientError
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class PrintError < Error
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class ScreenshotError < Error
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class ScriptInjectionError < Error
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class StyleInjectionError < Error
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class NotificationsError < Error
|
|
57
|
+
@causes: untyped
|
|
58
|
+
|
|
59
|
+
attr_reader causes: untyped
|
|
60
|
+
|
|
61
|
+
def initialize: (untyped causes) -> void
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class NavigationError < Error
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
class NavigationAuthError < NavigationError
|
|
68
|
+
@url: untyped
|
|
69
|
+
|
|
70
|
+
attr_reader url: untyped
|
|
71
|
+
|
|
72
|
+
def initialize: (untyped url, ?untyped? message) -> void
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class NavigationTimeoutError < NavigationError
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class NavigationNotFoundError < NavigationError
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
class NavigationDNSError < NavigationError
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
attr_accessor self.default_timeout: untyped
|
|
85
|
+
|
|
86
|
+
attr_accessor self.enable_default_logging_subscriber: untyped
|
|
87
|
+
|
|
88
|
+
attr_accessor self.log_truncate_limit: untyped
|
|
89
|
+
|
|
90
|
+
attr_accessor self.chromedriver_log_level: untyped
|
|
91
|
+
|
|
92
|
+
attr_reader self.logging_subscriber: untyped
|
|
93
|
+
|
|
94
|
+
attr_reader self.logger: untyped
|
|
95
|
+
|
|
96
|
+
attr_reader self.network_events_logger: untyped
|
|
97
|
+
|
|
98
|
+
attr_reader self.browser_console_logger: untyped
|
|
99
|
+
|
|
100
|
+
attr_reader self.notification_service: untyped
|
|
101
|
+
|
|
102
|
+
# Allow configuration through a block
|
|
103
|
+
def self.configure: () ?{ (untyped) -> untyped } -> untyped
|
|
104
|
+
|
|
105
|
+
def self.init: () -> untyped
|
|
106
|
+
|
|
107
|
+
# Truncates a value for safe log output - a raw url/param can be a `data:` URL whose base64
|
|
108
|
+
# payload is proportional to document size, and logging it whole can be large enough to choke
|
|
109
|
+
# CI log ingestion (confirmed live: GitHub Actions' log UI stalls badly on very long single
|
|
110
|
+
# lines, reading as a hung job even though the process underneath is fine).
|
|
111
|
+
#
|
|
112
|
+
# @param [Object] value The value to truncate (converted via #to_s).
|
|
113
|
+
# @param [Integer] limit The maximum number of bytes to keep. Defaults to
|
|
114
|
+
# +Bidi2pdf.log_truncate_limit+, itself configurable via +Bidi2pdf.configure+.
|
|
115
|
+
# @return [String] The value unchanged if short enough, otherwise a truncated prefix plus a
|
|
116
|
+
# byte-count marker. Truncation is byte-based (not character-based), since the goal is
|
|
117
|
+
# bounding actual log-entry size; a partial trailing multi-byte character is scrubbed rather
|
|
118
|
+
# than left as invalid UTF-8.
|
|
119
|
+
def self.truncate_for_log: (untyped value, ?limit: untyped) -> (untyped | ::String)
|
|
120
|
+
|
|
121
|
+
def self.translate_paper_format: (untyped format) -> { width: untyped, height: untyped }
|
|
122
|
+
|
|
123
|
+
def self.logger=: (untyped new_logger) -> untyped
|
|
124
|
+
|
|
125
|
+
def self.network_events_logger=: (untyped new_network_events_logger) -> untyped
|
|
126
|
+
|
|
127
|
+
def self.browser_console_logger=: (untyped new_browser_console_logger) -> untyped
|
|
128
|
+
|
|
129
|
+
def self.logging_subscriber=: (untyped new_logging_subscriber) -> untyped
|
|
130
|
+
|
|
131
|
+
def self.notification_service=: (untyped new_notification_service) -> untyped
|
|
4
132
|
end
|
data/tasks/generate_rbs.rake
CHANGED
|
@@ -6,7 +6,7 @@ require "rbs"
|
|
|
6
6
|
SOURCE_DIR = "lib"
|
|
7
7
|
OUTPUT_DIR = "sig"
|
|
8
8
|
|
|
9
|
-
# rubocop:disable Metrics/BlockLength
|
|
9
|
+
# rubocop:disable-next Metrics/BlockLength
|
|
10
10
|
namespace :rbs do
|
|
11
11
|
file_list = FileList["#{SOURCE_DIR}/**/*.rb"]
|
|
12
12
|
|
|
@@ -32,15 +32,11 @@ namespace :rbs do
|
|
|
32
32
|
parser = RBS::Prototype::RB.new
|
|
33
33
|
parser.parse input.read
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
puts "⚠️ RBS file already exists: #{rbs_path}"
|
|
37
|
-
else
|
|
38
|
-
puts "📝 Writing RBS file: #{rbs_path}"
|
|
35
|
+
puts "📝 Writing RBS file: #{rbs_path}"
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
end
|
|
37
|
+
output.open("w") do |io|
|
|
38
|
+
writer = RBS::Writer.new(out: io)
|
|
39
|
+
writer.write(parser.decls)
|
|
44
40
|
end
|
|
45
41
|
rescue StandardError => e
|
|
46
42
|
puts "❌ Error generating RBS for #{rb_path}: #{e.message}"
|
|
@@ -55,10 +51,14 @@ namespace :rbs do
|
|
|
55
51
|
|
|
56
52
|
desc "Clean all generated RBS files"
|
|
57
53
|
task :clean_rbs do
|
|
58
|
-
|
|
54
|
+
# sig/vendor/** holds hand-written signatures for third-party gems (e.g. Thor) with no
|
|
55
|
+
# corresponding lib/ source to regenerate from - excluded so a clean+regenerate cycle can't
|
|
56
|
+
# delete them with no way to get them back.
|
|
57
|
+
generated = FileList["#{OUTPUT_DIR}/**/*.rbs"].exclude("#{OUTPUT_DIR}/vendor/**")
|
|
58
|
+
|
|
59
|
+
generated.each do |file|
|
|
59
60
|
FileUtils.rm_f(file)
|
|
60
61
|
end
|
|
61
62
|
puts "🧹 Cleaned up all RBS files"
|
|
62
63
|
end
|
|
63
64
|
end
|
|
64
|
-
# rubocop:enable Metrics/BlockLength
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
# Works out which rubygems.org credential `rake release` would really push with, and refuses to go on
|
|
6
|
+
# when that is not what the repo's configuration says it should be.
|
|
7
|
+
module ReleaseCredentialsCheck
|
|
8
|
+
BUNDLE_CONFIG_PATH = ".bundle/config"
|
|
9
|
+
CREDENTIALS_PATH = File.expand_path("~/.gem/credentials")
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# Ask Bundler, exactly as its own release task does (gem_helper.rb: Bundler.settings["gem.push_key"])
|
|
14
|
+
# - never parse .bundle/config for this. Bundler may not be reading that file at all: the official
|
|
15
|
+
# ruby Docker images set BUNDLE_APP_CONFIG=/usr/local/bundle, which silently replaces the repo's
|
|
16
|
+
# .bundle/ as the app config dir. Reading the file directly made this check report a key that
|
|
17
|
+
# `rake release` then did not use.
|
|
18
|
+
def effective_key
|
|
19
|
+
Bundler.settings["gem.push_key"]&.to_s&.downcase
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# What the repo's own .bundle/config asks for - only ever compared against #effective_key.
|
|
23
|
+
def key_on_disk
|
|
24
|
+
return unless File.exist?(BUNDLE_CONFIG_PATH)
|
|
25
|
+
|
|
26
|
+
(YAML.load_file(BUNDLE_CONFIG_PATH) || {})["BUNDLE_GEM__PUSH_KEY"]&.to_s&.downcase
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def available_keys
|
|
30
|
+
File.exist?(CREDENTIALS_PATH) ? (YAML.load_file(CREDENTIALS_PATH) || {}).keys.map(&:to_s) : []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Always printed first, in one stream (puts only - mixing in `warn`'s stderr made lines print out
|
|
34
|
+
# of order once a terminal merges the two streams), so the context stays visible above whichever
|
|
35
|
+
# message stops the release.
|
|
36
|
+
def print_banner(key, keys)
|
|
37
|
+
puts "=== rubygems.org push credentials ==="
|
|
38
|
+
puts " gem.push_key Bundler will use: #{key || "(not set)"}"
|
|
39
|
+
puts " Bundler app config dir: #{Bundler.app_config_path}#{" (from BUNDLE_APP_CONFIG)" if ENV["BUNDLE_APP_CONFIG"]}"
|
|
40
|
+
puts " ~/.gem/credentials keys: #{keys.empty? ? "(none found)" : keys.join(", ")}"
|
|
41
|
+
puts "======================================"
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [String, nil] why the release must not go on, or nil when it may
|
|
45
|
+
def problem(key, keys)
|
|
46
|
+
ignored_config_problem(key) || credentials_problem(key, keys)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def ignored_config_problem(key)
|
|
50
|
+
wanted = key_on_disk
|
|
51
|
+
return if wanted.nil? || wanted == key
|
|
52
|
+
|
|
53
|
+
"#{BUNDLE_CONFIG_PATH} sets gem.push_key '#{wanted}', but Bundler is not reading that file " \
|
|
54
|
+
"(app config dir: #{Bundler.app_config_path}) and would push with '#{key || "the default rubygems_api_key"}'. " \
|
|
55
|
+
"Set BUNDLE_GEM__PUSH_KEY=#{wanted} in the environment, or unset BUNDLE_APP_CONFIG."
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def credentials_problem(key, keys)
|
|
59
|
+
return "~/.gem/credentials has no keys at all (missing, empty, or not mounted)." if keys.empty?
|
|
60
|
+
return "configured key '#{key}' is not among ~/.gem/credentials' keys #{keys.inspect}." if key && !keys.include?(key)
|
|
61
|
+
return if key || keys.include?("rubygems_api_key")
|
|
62
|
+
|
|
63
|
+
"no gem.push_key configured and ~/.gem/credentials has no default 'rubygems_api_key' key (found: #{keys.inspect})."
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
desc "Show which rubygems.org credential key `rake release`/`gem push` will use"
|
|
68
|
+
task :release_credentials_check do
|
|
69
|
+
# Without this, stdout is buffered (not a TTY once piped/captured) while stderr isn't - so
|
|
70
|
+
# `abort`'s message below can reach the terminal before the `puts` lines that logically ran
|
|
71
|
+
# first, once the two streams get merged. Confirmed live: without sync, "release aborted: ..."
|
|
72
|
+
# printed above the diagnostic banner, not after it.
|
|
73
|
+
$stdout.sync = true
|
|
74
|
+
|
|
75
|
+
key = ReleaseCredentialsCheck.effective_key
|
|
76
|
+
keys = ReleaseCredentialsCheck.available_keys
|
|
77
|
+
ReleaseCredentialsCheck.print_banner(key, keys)
|
|
78
|
+
|
|
79
|
+
problem = ReleaseCredentialsCheck.problem(key, keys)
|
|
80
|
+
abort "release aborted: #{problem}" if problem
|
|
81
|
+
|
|
82
|
+
puts "NOTE: no gem.push_key configured - this will push using the default :rubygems_api_key." if key.nil?
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# A prerequisite of "release:rubygem_push" specifically, not the outer "release" task. Bundler's
|
|
86
|
+
# own gem_helper.rb defines release as depending on
|
|
87
|
+
# ["build", "release:guard_clean", "release:source_control_push", "release:rubygem_push"], run in
|
|
88
|
+
# that listed order, with release:rubygem_push's own action being the actual `rubygem_push(...)`
|
|
89
|
+
# call that consumes this credential - attaching to the outer "release" task instead would append
|
|
90
|
+
# here via Task#enhance's simple union, running this *after* every one of those, including the
|
|
91
|
+
# push itself, which would make it a report on a mistake already made, not a guard against one.
|
|
92
|
+
# Attaching to "release:rubygem_push" guarantees Rake resolves this before *that* task's own
|
|
93
|
+
# action runs, regardless of how bundler orders its other prerequisites in some other version -
|
|
94
|
+
# automatically, regardless of how `rake release` gets invoked (this Makefile's release-shell, a
|
|
95
|
+
# bare host checkout, CI), not something you have to remember to check with a separate command.
|
|
96
|
+
desc "Push the built gem to rubygems.org (checks credentials first)"
|
|
97
|
+
task "release:rubygem_push" => :release_credentials_check
|