ratatui_ruby 0.8.0 → 0.9.0
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/.builds/ruby-3.2.yml +2 -2
- data/.builds/ruby-3.3.yml +2 -2
- data/.builds/ruby-3.4.yml +2 -2
- data/.builds/ruby-4.0.0.yml +2 -2
- data/.pre-commit-config.yaml +1 -1
- data/AGENTS.md +3 -3
- data/CHANGELOG.md +53 -1
- data/LICENSES/LGPL-3.0-or-later.txt +304 -0
- data/LICENSES/MIT-0.txt +16 -0
- data/README.md +33 -5
- data/Rakefile +1 -1
- data/doc/concepts/application_architecture.md +44 -3
- data/doc/concepts/application_testing.md +43 -1
- data/doc/concepts/async.md +32 -2
- data/doc/concepts/custom_widgets.md +247 -0
- data/doc/concepts/event_handling.md +32 -3
- data/doc/concepts/interactive_design.md +32 -2
- data/doc/contributors/auditing/parity.md +7 -1
- data/doc/contributors/design/ruby_frontend.md +85 -1
- data/doc/contributors/design/rust_backend.md +67 -1
- data/doc/contributors/developing_examples.md +56 -2
- data/doc/contributors/documentation_style.md +20 -3
- data/doc/contributors/future_work.md +169 -0
- data/doc/contributors/index.md +1 -1
- data/doc/contributors/v1.0.0_blockers.md +15 -175
- data/doc/getting_started/quickstart.md +22 -4
- data/doc/getting_started/why.md +1 -1
- data/doc/index.md +2 -1
- data/doc/troubleshooting/debugging.md +32 -2
- data/doc/troubleshooting/terminal_limitations.md +8 -2
- data/doc/troubleshooting/tui_output.md +42 -0
- data/examples/app_all_events/README.md +14 -2
- data/examples/app_all_events/app.rb +1 -1
- data/examples/app_all_events/model/app_model.rb +1 -1
- data/examples/app_all_events/model/event_color_cycle.rb +1 -1
- data/examples/app_all_events/model/event_entry.rb +1 -1
- data/examples/app_all_events/model/msg.rb +1 -1
- data/examples/app_all_events/model/timestamp.rb +1 -1
- data/examples/app_all_events/update.rb +1 -1
- data/examples/app_all_events/view/app_view.rb +1 -1
- data/examples/app_all_events/view/controls_view.rb +1 -1
- data/examples/app_all_events/view/counts_view.rb +1 -1
- data/examples/app_all_events/view/live_view.rb +1 -1
- data/examples/app_all_events/view/log_view.rb +1 -1
- data/examples/app_all_events/view.rb +1 -1
- data/examples/app_color_picker/README.md +20 -2
- data/examples/app_color_picker/app.rb +1 -1
- data/examples/app_color_picker/clipboard.rb +1 -1
- data/examples/app_color_picker/color.rb +1 -1
- data/examples/app_color_picker/controls.rb +1 -1
- data/examples/app_color_picker/copy_dialog.rb +1 -1
- data/examples/app_color_picker/export_pane.rb +1 -1
- data/examples/app_color_picker/harmony.rb +1 -1
- data/examples/app_color_picker/input.rb +1 -1
- data/examples/app_color_picker/main_container.rb +1 -1
- data/examples/app_color_picker/palette.rb +1 -1
- data/examples/app_login_form/README.md +8 -2
- data/examples/app_login_form/app.rb +1 -1
- data/examples/app_stateful_interaction/README.md +2 -2
- data/examples/app_stateful_interaction/app.rb +71 -17
- data/examples/timeout_demo.rb +1 -1
- data/examples/verify_quickstart_dsl/README.md +6 -0
- data/examples/verify_quickstart_dsl/app.rb +3 -3
- data/examples/verify_quickstart_layout/README.md +6 -0
- data/examples/verify_quickstart_layout/app.rb +3 -3
- data/examples/verify_quickstart_lifecycle/README.md +6 -0
- data/examples/verify_quickstart_lifecycle/app.rb +3 -3
- data/examples/verify_readme_usage/README.md +6 -0
- data/examples/verify_readme_usage/app.rb +3 -3
- data/examples/widget_barchart/README.md +6 -0
- data/examples/widget_barchart/app.rb +2 -2
- data/examples/widget_block/README.md +7 -1
- data/examples/widget_block/app.rb +2 -2
- data/examples/widget_box/README.md +6 -0
- data/examples/widget_box/app.rb +9 -6
- data/examples/widget_calendar/README.md +6 -0
- data/examples/widget_calendar/app.rb +2 -2
- data/examples/widget_canvas/README.md +4 -0
- data/examples/widget_canvas/app.rb +2 -2
- data/examples/widget_cell/README.md +6 -0
- data/examples/widget_cell/app.rb +2 -3
- data/examples/widget_center/README.md +4 -0
- data/examples/widget_center/app.rb +2 -2
- data/examples/widget_chart/README.md +6 -0
- data/examples/widget_chart/app.rb +2 -2
- data/examples/widget_gauge/README.md +6 -0
- data/examples/widget_gauge/app.rb +2 -2
- data/examples/widget_layout_split/README.md +6 -0
- data/examples/widget_layout_split/app.rb +3 -3
- data/examples/widget_line_gauge/README.md +6 -0
- data/examples/widget_line_gauge/app.rb +2 -2
- data/examples/widget_list/README.md +6 -0
- data/examples/widget_list/app.rb +2 -2
- data/examples/widget_map/README.md +8 -2
- data/examples/widget_map/app.rb +2 -2
- data/examples/widget_overlay/README.md +7 -1
- data/examples/widget_overlay/app.rb +2 -2
- data/examples/widget_popup/README.md +6 -0
- data/examples/widget_popup/app.rb +2 -2
- data/examples/widget_ratatui_logo/README.md +6 -0
- data/examples/widget_ratatui_logo/app.rb +2 -3
- data/examples/widget_ratatui_mascot/README.md +6 -0
- data/examples/widget_ratatui_mascot/app.rb +2 -2
- data/examples/widget_rect/README.md +12 -0
- data/examples/widget_rect/app.rb +40 -26
- data/examples/widget_render/README.md +6 -0
- data/examples/widget_render/app.rb +2 -2
- data/examples/widget_render/app.rbs +41 -0
- data/examples/widget_rich_text/README.md +6 -0
- data/examples/widget_rich_text/app.rb +2 -2
- data/examples/widget_scroll_text/README.md +6 -0
- data/examples/widget_scroll_text/app.rb +2 -2
- data/examples/widget_scrollbar/README.md +6 -0
- data/examples/widget_scrollbar/app.rb +2 -2
- data/examples/widget_sparkline/README.md +6 -0
- data/examples/widget_sparkline/app.rb +2 -2
- data/examples/widget_style_colors/README.md +6 -0
- data/examples/widget_style_colors/app.rb +2 -2
- data/examples/widget_table/README.md +8 -2
- data/examples/widget_table/app.rb +2 -2
- data/examples/widget_tabs/README.md +6 -0
- data/examples/widget_tabs/app.rb +2 -2
- data/examples/widget_text_width/README.md +6 -0
- data/examples/widget_text_width/app.rb +4 -4
- data/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/ext/ratatui_ruby/extconf.rb +2 -2
- data/ext/ratatui_ruby/src/rendering.rs +1 -1
- data/ext/ratatui_ruby/src/style.rs +0 -8
- data/ext/ratatui_ruby/src/widgets/chart.rs +0 -118
- data/ext/ratatui_ruby/src/widgets/list_state.rs +36 -0
- data/lib/ratatui_ruby/buffer/cell.rb +34 -2
- data/lib/ratatui_ruby/buffer.rb +2 -2
- data/lib/ratatui_ruby/cell.rb +34 -2
- data/lib/ratatui_ruby/event/focus_gained.rb +26 -2
- data/lib/ratatui_ruby/event/focus_lost.rb +26 -2
- data/lib/ratatui_ruby/event/key/character.rb +18 -2
- data/lib/ratatui_ruby/event/key/media.rb +2 -2
- data/lib/ratatui_ruby/event/key/modifier.rb +10 -2
- data/lib/ratatui_ruby/event/key/navigation.rb +2 -2
- data/lib/ratatui_ruby/event/key/system.rb +2 -2
- data/lib/ratatui_ruby/event/key.rb +114 -2
- data/lib/ratatui_ruby/event/mouse.rb +42 -2
- data/lib/ratatui_ruby/event/none.rb +10 -2
- data/lib/ratatui_ruby/event/paste.rb +34 -2
- data/lib/ratatui_ruby/event/resize.rb +34 -2
- data/lib/ratatui_ruby/event.rb +26 -2
- data/lib/ratatui_ruby/frame.rb +74 -2
- data/lib/ratatui_ruby/layout/constraint.rb +58 -2
- data/lib/ratatui_ruby/layout/layout.rb +47 -2
- data/lib/ratatui_ruby/layout/rect.rb +403 -2
- data/lib/ratatui_ruby/layout.rb +2 -2
- data/lib/ratatui_ruby/list_state.rb +113 -2
- data/lib/ratatui_ruby/output_guard.rb +26 -3
- data/lib/ratatui_ruby/schema/bar_chart/bar.rb +2 -2
- data/lib/ratatui_ruby/schema/bar_chart/bar_group.rb +2 -2
- data/lib/ratatui_ruby/schema/bar_chart.rb +50 -2
- data/lib/ratatui_ruby/schema/block.rb +21 -15
- data/lib/ratatui_ruby/schema/calendar.rb +2 -2
- data/lib/ratatui_ruby/schema/canvas.rb +10 -2
- data/lib/ratatui_ruby/schema/center.rb +10 -2
- data/lib/ratatui_ruby/schema/chart.rb +2 -28
- data/lib/ratatui_ruby/schema/clear.rb +10 -2
- data/lib/ratatui_ruby/schema/constraint.rb +58 -2
- data/lib/ratatui_ruby/schema/cursor.rb +10 -2
- data/lib/ratatui_ruby/schema/draw.rb +10 -2
- data/lib/ratatui_ruby/schema/gauge.rb +2 -2
- data/lib/ratatui_ruby/schema/layout.rb +18 -2
- data/lib/ratatui_ruby/schema/line_gauge.rb +2 -2
- data/lib/ratatui_ruby/schema/list.rb +10 -2
- data/lib/ratatui_ruby/schema/list_item.rb +10 -2
- data/lib/ratatui_ruby/schema/overlay.rb +10 -2
- data/lib/ratatui_ruby/schema/paragraph.rb +10 -2
- data/lib/ratatui_ruby/schema/ratatui_logo.rb +2 -2
- data/lib/ratatui_ruby/schema/ratatui_mascot.rb +2 -2
- data/lib/ratatui_ruby/schema/rect.rb +58 -2
- data/lib/ratatui_ruby/schema/row.rb +10 -2
- data/lib/ratatui_ruby/schema/scrollbar.rb +2 -2
- data/lib/ratatui_ruby/schema/shape/label.rb +10 -2
- data/lib/ratatui_ruby/schema/sparkline.rb +10 -2
- data/lib/ratatui_ruby/schema/style.rb +18 -2
- data/lib/ratatui_ruby/schema/table.rb +2 -2
- data/lib/ratatui_ruby/schema/tabs.rb +2 -2
- data/lib/ratatui_ruby/schema/text.rb +34 -2
- data/lib/ratatui_ruby/scrollbar_state.rb +10 -2
- data/lib/ratatui_ruby/style/style.rb +18 -2
- data/lib/ratatui_ruby/style.rb +2 -2
- data/lib/ratatui_ruby/table_state.rb +10 -2
- data/lib/ratatui_ruby/terminal_lifecycle.rb +18 -3
- data/lib/ratatui_ruby/test_helper/event_injection.rb +34 -2
- data/lib/ratatui_ruby/test_helper/snapshot.rb +74 -9
- data/lib/ratatui_ruby/test_helper/style_assertions.rb +98 -2
- data/lib/ratatui_ruby/test_helper/terminal.rb +50 -2
- data/lib/ratatui_ruby/test_helper/test_doubles.rb +18 -2
- data/lib/ratatui_ruby/test_helper.rb +10 -2
- data/lib/ratatui_ruby/tui/buffer_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/canvas_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/core.rb +2 -2
- data/lib/ratatui_ruby/tui/layout_factories.rb +32 -2
- data/lib/ratatui_ruby/tui/state_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/style_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/text_factories.rb +2 -2
- data/lib/ratatui_ruby/tui/widget_factories.rb +2 -2
- data/lib/ratatui_ruby/tui.rb +11 -3
- data/lib/ratatui_ruby/version.rb +3 -3
- data/lib/ratatui_ruby/widgets/bar_chart/bar.rb +2 -2
- data/lib/ratatui_ruby/widgets/bar_chart/bar_group.rb +2 -2
- data/lib/ratatui_ruby/widgets/bar_chart.rb +58 -2
- data/lib/ratatui_ruby/widgets/block.rb +37 -15
- data/lib/ratatui_ruby/widgets/calendar.rb +2 -2
- data/lib/ratatui_ruby/widgets/canvas.rb +10 -2
- data/lib/ratatui_ruby/widgets/cell.rb +10 -2
- data/lib/ratatui_ruby/widgets/center.rb +10 -2
- data/lib/ratatui_ruby/widgets/chart.rb +2 -28
- data/lib/ratatui_ruby/widgets/clear.rb +10 -2
- data/lib/ratatui_ruby/widgets/cursor.rb +10 -2
- data/lib/ratatui_ruby/widgets/gauge.rb +16 -2
- data/lib/ratatui_ruby/widgets/line_gauge.rb +16 -2
- data/lib/ratatui_ruby/widgets/list.rb +41 -2
- data/lib/ratatui_ruby/widgets/list_item.rb +10 -2
- data/lib/ratatui_ruby/widgets/overlay.rb +10 -2
- data/lib/ratatui_ruby/widgets/paragraph.rb +10 -2
- data/lib/ratatui_ruby/widgets/ratatui_logo.rb +2 -2
- data/lib/ratatui_ruby/widgets/ratatui_mascot.rb +2 -2
- data/lib/ratatui_ruby/widgets/row.rb +10 -2
- data/lib/ratatui_ruby/widgets/scrollbar.rb +2 -2
- data/lib/ratatui_ruby/widgets/shape/label.rb +10 -2
- data/lib/ratatui_ruby/widgets/sparkline.rb +10 -2
- data/lib/ratatui_ruby/widgets/table.rb +62 -2
- data/lib/ratatui_ruby/widgets/tabs.rb +2 -2
- data/lib/ratatui_ruby/widgets.rb +2 -2
- data/lib/ratatui_ruby.rb +90 -2
- data/sig/examples/app_all_events/view.rbs +7 -1
- data/sig/examples/app_all_events/view_state.rbs +7 -1
- data/sig/examples/app_color_picker/app.rbs +5 -0
- data/sig/examples/app_stateful_interaction/app.rbs +7 -1
- data/sig/examples/verify_quickstart_dsl/app.rbs +7 -1
- data/sig/examples/verify_quickstart_lifecycle/app.rbs +7 -1
- data/sig/examples/verify_readme_usage/app.rbs +7 -1
- data/sig/examples/widget_block_demo/app.rbs +6 -0
- data/sig/examples/widget_box_demo/app.rbs +7 -1
- data/sig/examples/widget_calendar_demo/app.rbs +7 -1
- data/sig/examples/widget_cell_demo/app.rbs +7 -1
- data/sig/examples/widget_chart_demo/app.rbs +7 -1
- data/sig/examples/widget_gauge_demo/app.rbs +7 -1
- data/sig/examples/widget_layout_split/app.rbs +7 -1
- data/sig/examples/widget_line_gauge_demo/app.rbs +7 -1
- data/sig/examples/widget_list_demo/app.rbs +5 -0
- data/sig/examples/widget_map_demo/app.rbs +7 -1
- data/sig/examples/widget_popup_demo/app.rbs +7 -1
- data/sig/examples/widget_ratatui_logo_demo/app.rbs +7 -1
- data/sig/examples/widget_ratatui_mascot_demo/app.rbs +7 -1
- data/sig/examples/widget_rect/app.rbs +7 -1
- data/sig/examples/widget_render/app.rbs +7 -1
- data/sig/examples/widget_rich_text/app.rbs +7 -1
- data/sig/examples/widget_scroll_text/app.rbs +7 -1
- data/sig/examples/widget_scrollbar_demo/app.rbs +7 -1
- data/sig/examples/widget_sparkline_demo/app.rbs +7 -1
- data/sig/examples/widget_style_colors/app.rbs +7 -1
- data/sig/examples/widget_table_demo/app.rbs +7 -1
- data/sig/examples/widget_text_width/app.rbs +7 -1
- data/sig/ratatui_ruby/event.rbs +7 -1
- data/sig/ratatui_ruby/frame.rbs +15 -3
- data/sig/ratatui_ruby/list_state.rbs +11 -1
- data/sig/ratatui_ruby/ratatui_ruby.rbs +8 -2
- data/sig/ratatui_ruby/schema/bar_chart/bar.rbs +7 -1
- data/sig/ratatui_ruby/schema/bar_chart/bar_group.rbs +6 -0
- data/sig/ratatui_ruby/schema/bar_chart.rbs +6 -0
- data/sig/ratatui_ruby/schema/block.rbs +7 -1
- data/sig/ratatui_ruby/schema/calendar.rbs +6 -0
- data/sig/ratatui_ruby/schema/canvas.rbs +6 -0
- data/sig/ratatui_ruby/schema/center.rbs +6 -0
- data/sig/ratatui_ruby/schema/chart.rbs +6 -9
- data/sig/ratatui_ruby/schema/constraint.rbs +6 -0
- data/sig/ratatui_ruby/schema/cursor.rbs +6 -0
- data/sig/ratatui_ruby/schema/draw.rbs +6 -0
- data/sig/ratatui_ruby/schema/gauge.rbs +9 -1
- data/sig/ratatui_ruby/schema/layout.rbs +6 -0
- data/sig/ratatui_ruby/schema/line_gauge.rbs +9 -1
- data/sig/ratatui_ruby/schema/list.rbs +9 -1
- data/sig/ratatui_ruby/schema/list_item.rbs +7 -1
- data/sig/ratatui_ruby/schema/overlay.rbs +6 -0
- data/sig/ratatui_ruby/schema/paragraph.rbs +6 -0
- data/sig/ratatui_ruby/schema/ratatui_logo.rbs +6 -0
- data/sig/ratatui_ruby/schema/ratatui_mascot.rbs +5 -0
- data/sig/ratatui_ruby/schema/rect.rbs +30 -0
- data/sig/ratatui_ruby/schema/row.rbs +7 -1
- data/sig/ratatui_ruby/schema/scrollbar.rbs +6 -0
- data/sig/ratatui_ruby/schema/sparkline.rbs +6 -0
- data/sig/ratatui_ruby/schema/style.rbs +7 -1
- data/sig/ratatui_ruby/schema/table.rbs +11 -1
- data/sig/ratatui_ruby/schema/tabs.rbs +6 -0
- data/sig/ratatui_ruby/schema/text.rbs +7 -1
- data/sig/ratatui_ruby/scrollbar_state.rbs +7 -1
- data/sig/ratatui_ruby/session.rbs +7 -1
- data/sig/ratatui_ruby/table_state.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/event_injection.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/snapshot.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/style_assertions.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/terminal.rbs +7 -1
- data/sig/ratatui_ruby/test_helper/test_doubles.rbs +7 -1
- data/sig/ratatui_ruby/test_helper.rbs +7 -1
- data/sig/ratatui_ruby/tui/buffer_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/canvas_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/core.rbs +7 -1
- data/sig/ratatui_ruby/tui/layout_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/state_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/style_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/text_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui/widget_factories.rbs +7 -1
- data/sig/ratatui_ruby/tui.rbs +7 -1
- data/sig/ratatui_ruby/version.rbs +6 -0
- data/tasks/autodoc/examples.rb +1 -1
- data/tasks/autodoc/member.rb +1 -1
- data/tasks/autodoc/name.rb +1 -1
- data/tasks/bump/cargo_lockfile.rb +1 -1
- data/tasks/bump/changelog.rb +1 -1
- data/tasks/bump/header.rb +1 -1
- data/tasks/bump/history.rb +1 -1
- data/tasks/bump/links.rb +1 -1
- data/tasks/bump/manifest.rb +1 -1
- data/tasks/bump/ruby_gem.rb +1 -1
- data/tasks/bump/sem_ver.rb +1 -1
- data/tasks/bump/unreleased_section.rb +1 -1
- data/tasks/license/headers_md.rb +223 -0
- data/tasks/license/headers_rb.rb +210 -0
- data/tasks/license/license_utils.rb +130 -0
- data/tasks/license/snippets_md.rb +315 -0
- data/tasks/license/snippets_rdoc.rb +150 -0
- data/tasks/license.rake +91 -0
- data/tasks/rdoc_config.rb +1 -1
- data/tasks/resources/build.yml.erb +13 -7
- data/tasks/sourcehut.rake +3 -1
- data/tasks/terminal_preview/app_screenshot.rb +1 -1
- data/tasks/terminal_preview/crash_report.rb +1 -1
- data/tasks/terminal_preview/example_app.rb +1 -1
- data/tasks/terminal_preview/launcher_script.rb +1 -1
- data/tasks/terminal_preview/preview_collection.rb +1 -1
- data/tasks/terminal_preview/preview_timing.rb +1 -1
- data/tasks/terminal_preview/safety_confirmation.rb +1 -1
- data/tasks/terminal_preview/saved_screenshot.rb +1 -1
- data/tasks/terminal_preview/system_appearance.rb +1 -1
- data/tasks/terminal_preview/terminal_window.rb +1 -1
- data/tasks/terminal_preview/window_id.rb +1 -1
- data/tasks/website/index_page.rb +1 -1
- data/tasks/website/version.rb +1 -1
- data/tasks/website/version_menu.rb +1 -1
- data/tasks/website/versioned_documentation.rb +1 -1
- data/tasks/website/website.rb +1 -1
- metadata +13 -3
- data/doc/migration/v0_7_0.md +0 -236
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
# Script to add SPDX snippet headers to fenced code blocks in markdown files.
|
|
9
|
+
#
|
|
10
|
+
# Usage: ruby tasks/license/snippets_md.rb [path...]
|
|
11
|
+
#
|
|
12
|
+
# If no paths are given, processes all .md files via git ls-files.
|
|
13
|
+
#
|
|
14
|
+
# Rules:
|
|
15
|
+
# - Wraps all fenced code blocks (``` or ````) with SPDX snippet headers (MIT-0)
|
|
16
|
+
# - For SYNC:START/SYNC:END blocks, wraps AROUND the sync markers (not inside)
|
|
17
|
+
# - Uses git blame to determine the latest edit year for the code lines
|
|
18
|
+
# - Skips blocks that are already properly wrapped with MIT-0 and Kerrick Long
|
|
19
|
+
# - Removes malformed existing SPDX-Snippet blocks and replaces with correct ones
|
|
20
|
+
|
|
21
|
+
require "open3"
|
|
22
|
+
require "date"
|
|
23
|
+
|
|
24
|
+
COPYRIGHT_HOLDER = "Kerrick Long"
|
|
25
|
+
LICENSE = "MIT-0"
|
|
26
|
+
|
|
27
|
+
# Files to skip entirely (relative paths from repo root)
|
|
28
|
+
EXCLUDED_FILES = [
|
|
29
|
+
"doc/contributors/v1.0.0_blockers.md",
|
|
30
|
+
"doc/contributors/upstream_requests/tab_rects.md",
|
|
31
|
+
"doc/contributors/upstream_requests/title_rects.md",
|
|
32
|
+
].freeze
|
|
33
|
+
|
|
34
|
+
def get_latest_git_year(file, start_line, end_line)
|
|
35
|
+
cmd = %W[git blame -L #{start_line},#{end_line} --date=short -- #{file}]
|
|
36
|
+
output, _status = Open3.capture2(*cmd)
|
|
37
|
+
years = output.scan(/(\d{4})-\d{2}-\d{2}/).flatten.map(&:to_i)
|
|
38
|
+
years.empty? ? Date.today.year : years.max
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def is_our_snippet_header?(lines, idx)
|
|
42
|
+
# Check if the current SPDX-SnippetBegin block already has our copyright/license
|
|
43
|
+
i = idx + 1
|
|
44
|
+
has_our_copyright = false
|
|
45
|
+
has_mit0 = false
|
|
46
|
+
|
|
47
|
+
while i < lines.length && !lines[i].include?("-->")
|
|
48
|
+
line = lines[i]
|
|
49
|
+
has_our_copyright = true if line.include?(COPYRIGHT_HOLDER) && line.include?("SPDX-FileCopyrightText")
|
|
50
|
+
has_mit0 = true if line.include?("MIT-0") && line.include?("SPDX-License-Identifier")
|
|
51
|
+
i += 1
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
has_our_copyright && has_mit0
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def find_snippet_end(lines, start_idx)
|
|
58
|
+
i = start_idx
|
|
59
|
+
while i < lines.length
|
|
60
|
+
return i if lines[i].include?("SPDX-SnippetEnd")
|
|
61
|
+
i += 1
|
|
62
|
+
end
|
|
63
|
+
nil
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def process_file(filepath)
|
|
67
|
+
# Skip excluded files
|
|
68
|
+
return if EXCLUDED_FILES.any? { |excluded| filepath.end_with?(excluded) }
|
|
69
|
+
|
|
70
|
+
content = File.read(filepath)
|
|
71
|
+
lines = content.lines
|
|
72
|
+
|
|
73
|
+
# Track code block ranges (to exclude from file header year calculation)
|
|
74
|
+
code_block_ranges = []
|
|
75
|
+
changes = []
|
|
76
|
+
removals = [] # existing malformed SPDX snippet blocks to remove
|
|
77
|
+
i = 0
|
|
78
|
+
|
|
79
|
+
while i < lines.length
|
|
80
|
+
line = lines[i]
|
|
81
|
+
|
|
82
|
+
# Check if we're at an existing SPDX-SnippetBegin
|
|
83
|
+
if line.include?("SPDX-SnippetBegin")
|
|
84
|
+
snippet_start = i
|
|
85
|
+
snippet_end = find_snippet_end(lines, i)
|
|
86
|
+
|
|
87
|
+
if snippet_end
|
|
88
|
+
# Check if this is already our proper snippet
|
|
89
|
+
if is_our_snippet_header?(lines, i)
|
|
90
|
+
# Skip this block entirely - it's already correct
|
|
91
|
+
i = snippet_end + 1
|
|
92
|
+
next
|
|
93
|
+
else
|
|
94
|
+
# Mark for removal - we'll re-wrap the inner content
|
|
95
|
+
removals << { start: snippet_start, end: snippet_end }
|
|
96
|
+
i = snippet_end + 1
|
|
97
|
+
next
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Check for SYNC:START pattern
|
|
103
|
+
if line =~ /<!--\s*SYNC:START/
|
|
104
|
+
sync_start_line = i
|
|
105
|
+
j = i + 1
|
|
106
|
+
code_start = nil
|
|
107
|
+
code_end = nil
|
|
108
|
+
sync_end_line = nil
|
|
109
|
+
|
|
110
|
+
while j < lines.length
|
|
111
|
+
if lines[j] =~ /^(````*)(\w*)$/
|
|
112
|
+
if code_start.nil?
|
|
113
|
+
code_start = j
|
|
114
|
+
else
|
|
115
|
+
code_end = j
|
|
116
|
+
end
|
|
117
|
+
elsif lines[j] =~ /<!--\s*SYNC:END/
|
|
118
|
+
sync_end_line = j
|
|
119
|
+
break
|
|
120
|
+
end
|
|
121
|
+
j += 1
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
if code_start && code_end && sync_end_line
|
|
125
|
+
year = get_latest_git_year(filepath, code_start + 1, code_end + 1)
|
|
126
|
+
changes << {
|
|
127
|
+
type: :sync_block,
|
|
128
|
+
start: sync_start_line,
|
|
129
|
+
end: sync_end_line,
|
|
130
|
+
year:,
|
|
131
|
+
}
|
|
132
|
+
code_block_ranges << (code_start..code_end)
|
|
133
|
+
i = sync_end_line + 1
|
|
134
|
+
next
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Check for standalone fenced code block
|
|
139
|
+
if line =~ /^(````*)(\w*)$/
|
|
140
|
+
fence_marker = $1
|
|
141
|
+
fence_start = i
|
|
142
|
+
re_end = /^#{Regexp.escape(fence_marker)}$/
|
|
143
|
+
|
|
144
|
+
j = i + 1
|
|
145
|
+
fence_end = nil
|
|
146
|
+
while j < lines.length
|
|
147
|
+
if lines[j] =~ re_end
|
|
148
|
+
fence_end = j
|
|
149
|
+
break
|
|
150
|
+
end
|
|
151
|
+
j += 1
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
if fence_end
|
|
155
|
+
year = get_latest_git_year(filepath, fence_start + 1, fence_end + 1)
|
|
156
|
+
changes << {
|
|
157
|
+
type: :code_block,
|
|
158
|
+
start: fence_start,
|
|
159
|
+
end: fence_end,
|
|
160
|
+
year:,
|
|
161
|
+
}
|
|
162
|
+
code_block_ranges << (fence_start..fence_end)
|
|
163
|
+
i = fence_end + 1
|
|
164
|
+
next
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
i += 1
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Handle removals and additions
|
|
172
|
+
has_changes = !changes.empty? || !removals.empty?
|
|
173
|
+
|
|
174
|
+
# Remove existing malformed SPDX blocks (in reverse order)
|
|
175
|
+
removals.sort_by { |r| -r[:start] }.each do |removal|
|
|
176
|
+
# Remove the SnippetEnd line
|
|
177
|
+
lines.delete_at(removal[:end])
|
|
178
|
+
# Remove lines from SnippetBegin through the --> closing the comment
|
|
179
|
+
close_idx = removal[:start]
|
|
180
|
+
while close_idx < lines.length && !lines[close_idx].include?("-->")
|
|
181
|
+
close_idx += 1
|
|
182
|
+
end
|
|
183
|
+
# Remove from start to close_idx inclusive
|
|
184
|
+
(close_idx - removal[:start] + 1).times { lines.delete_at(removal[:start]) }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Recalculate content after removals
|
|
188
|
+
content = lines.join
|
|
189
|
+
lines = content.lines
|
|
190
|
+
|
|
191
|
+
# Re-scan for code blocks that need wrapping
|
|
192
|
+
changes = []
|
|
193
|
+
i = 0
|
|
194
|
+
|
|
195
|
+
while i < lines.length
|
|
196
|
+
line = lines[i]
|
|
197
|
+
|
|
198
|
+
# Skip if already inside an SPDX-SnippetBegin block
|
|
199
|
+
if line.include?("SPDX-SnippetBegin")
|
|
200
|
+
while i < lines.length && !lines[i].include?("SPDX-SnippetEnd")
|
|
201
|
+
i += 1
|
|
202
|
+
end
|
|
203
|
+
i += 1
|
|
204
|
+
next
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Check for SYNC:START pattern
|
|
208
|
+
if line =~ /<!--\s*SYNC:START/
|
|
209
|
+
sync_start_line = i
|
|
210
|
+
j = i + 1
|
|
211
|
+
code_start = nil
|
|
212
|
+
code_end = nil
|
|
213
|
+
sync_end_line = nil
|
|
214
|
+
|
|
215
|
+
while j < lines.length
|
|
216
|
+
if lines[j] =~ /^(````*)(\w*)$/
|
|
217
|
+
if code_start.nil?
|
|
218
|
+
code_start = j
|
|
219
|
+
else
|
|
220
|
+
code_end = j
|
|
221
|
+
end
|
|
222
|
+
elsif lines[j] =~ /<!--\s*SYNC:END/
|
|
223
|
+
sync_end_line = j
|
|
224
|
+
break
|
|
225
|
+
end
|
|
226
|
+
j += 1
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
if code_start && code_end && sync_end_line
|
|
230
|
+
year = get_latest_git_year(filepath, code_start + 1, code_end + 1)
|
|
231
|
+
changes << {
|
|
232
|
+
type: :sync_block,
|
|
233
|
+
start: sync_start_line,
|
|
234
|
+
end: sync_end_line,
|
|
235
|
+
year:,
|
|
236
|
+
}
|
|
237
|
+
i = sync_end_line + 1
|
|
238
|
+
next
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Check for standalone fenced code block
|
|
243
|
+
if line =~ /^(````*)(\w*)$/
|
|
244
|
+
fence_marker = $1
|
|
245
|
+
fence_start = i
|
|
246
|
+
re_end = /^#{Regexp.escape(fence_marker)}$/
|
|
247
|
+
|
|
248
|
+
j = i + 1
|
|
249
|
+
fence_end = nil
|
|
250
|
+
while j < lines.length
|
|
251
|
+
if lines[j] =~ re_end
|
|
252
|
+
fence_end = j
|
|
253
|
+
break
|
|
254
|
+
end
|
|
255
|
+
j += 1
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
if fence_end
|
|
259
|
+
year = get_latest_git_year(filepath, fence_start + 1, fence_end + 1)
|
|
260
|
+
changes << {
|
|
261
|
+
type: :code_block,
|
|
262
|
+
start: fence_start,
|
|
263
|
+
end: fence_end,
|
|
264
|
+
year:,
|
|
265
|
+
}
|
|
266
|
+
i = fence_end + 1
|
|
267
|
+
next
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
i += 1
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
return if changes.empty? && !has_changes
|
|
275
|
+
|
|
276
|
+
# Apply changes in reverse order to preserve line numbers
|
|
277
|
+
changes.sort_by { |c| -c[:start] }.each do |change|
|
|
278
|
+
# REUSE-IgnoreStart
|
|
279
|
+
snippet_begin = "<!-- SPDX-SnippetBegin -->\n<!--\n SPDX-FileCopyrightText: #{change[:year]} #{COPYRIGHT_HOLDER}\n SPDX-License-Identifier: #{LICENSE}\n-->\n"
|
|
280
|
+
snippet_end = "<!-- SPDX-SnippetEnd -->\n"
|
|
281
|
+
# REUSE-IgnoreEnd
|
|
282
|
+
|
|
283
|
+
# Insert end marker after the block
|
|
284
|
+
lines.insert(change[:end] + 1, snippet_end)
|
|
285
|
+
# Insert begin marker before the block
|
|
286
|
+
lines.insert(change[:start], snippet_begin)
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
File.write(filepath, lines.join)
|
|
290
|
+
puts "Updated: #{filepath} (#{changes.length} code block(s))"
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def find_md_files(paths)
|
|
294
|
+
# Use git ls-files to respect .gitignore
|
|
295
|
+
if paths.empty?
|
|
296
|
+
`git ls-files '*.md'`.split("\n")
|
|
297
|
+
else
|
|
298
|
+
paths.flat_map do |path|
|
|
299
|
+
if File.directory?(path)
|
|
300
|
+
`git ls-files '#{path}/**/*.md'`.split("\n")
|
|
301
|
+
else
|
|
302
|
+
path
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
if __FILE__ == $0
|
|
309
|
+
paths = ARGV.empty? ? [] : ARGV
|
|
310
|
+
files = find_md_files(paths)
|
|
311
|
+
|
|
312
|
+
files.each do |file|
|
|
313
|
+
process_file(file)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
# Script to add SPDX snippet headers to RDoc code examples in Ruby files.
|
|
9
|
+
#
|
|
10
|
+
# Usage: ruby scripts/add_spdx_rdoc_snippets.rb [path...]
|
|
11
|
+
#
|
|
12
|
+
# If no paths are given, processes all .rb files via git ls-files.
|
|
13
|
+
#
|
|
14
|
+
# Rules:
|
|
15
|
+
# - Wraps RDoc code examples (indented comment lines) with SPDX snippet headers
|
|
16
|
+
# - Uses #-- and #++ to hide the SPDX headers from RDoc rendering
|
|
17
|
+
# - Uses git blame to determine the latest edit year for the code lines
|
|
18
|
+
# - Skips blocks that are already wrapped with SPDX-SnippetBegin
|
|
19
|
+
|
|
20
|
+
require "open3"
|
|
21
|
+
require "date"
|
|
22
|
+
|
|
23
|
+
COPYRIGHT_HOLDER = "Kerrick Long"
|
|
24
|
+
LICENSE = "MIT-0"
|
|
25
|
+
|
|
26
|
+
def get_latest_git_year(file, start_line, end_line)
|
|
27
|
+
cmd = %W[git blame -L #{start_line},#{end_line} --date=short -- #{file}]
|
|
28
|
+
output, _status = Open3.capture2(*cmd)
|
|
29
|
+
years = output.scan(/(\d{4})-\d{2}-\d{2}/).flatten.map(&:to_i)
|
|
30
|
+
years.empty? ? Date.today.year : years.max
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def find_rdoc_code_blocks(lines)
|
|
34
|
+
# Find all RDoc code blocks (indented comment lines)
|
|
35
|
+
# Returns array of {start:, end:, indent:} where indent is the comment prefix
|
|
36
|
+
blocks = []
|
|
37
|
+
i = 0
|
|
38
|
+
|
|
39
|
+
while i < lines.length
|
|
40
|
+
line = lines[i]
|
|
41
|
+
|
|
42
|
+
# Check if this is an indented code line in a comment
|
|
43
|
+
# Pattern: optional leading whitespace, #, then 3+ spaces (RDoc code indent)
|
|
44
|
+
if line =~ /^(\s*)#( +)(\S.*)$/
|
|
45
|
+
prefix = $1 # leading whitespace before #
|
|
46
|
+
block_start = i
|
|
47
|
+
|
|
48
|
+
# Find the extent of this code block
|
|
49
|
+
j = i
|
|
50
|
+
while j < lines.length
|
|
51
|
+
current = lines[j]
|
|
52
|
+
# Code block continues if line is indented code OR empty comment line
|
|
53
|
+
if current =~ /^#{Regexp.escape(prefix)}#( +|\s*$)/
|
|
54
|
+
j += 1
|
|
55
|
+
else
|
|
56
|
+
break
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
block_end = j - 1
|
|
61
|
+
|
|
62
|
+
# Only count as a block if it has actual code (not just empty lines)
|
|
63
|
+
has_code = (block_start..block_end).any? { |k| lines[k] =~ /^#{Regexp.escape(prefix)}# +\S/ }
|
|
64
|
+
|
|
65
|
+
if has_code && block_end > block_start
|
|
66
|
+
blocks << { start: block_start, end: block_end, prefix: }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
i = j
|
|
70
|
+
else
|
|
71
|
+
i += 1
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
blocks
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def is_already_wrapped?(lines, block_start, prefix)
|
|
79
|
+
# Check if the line before the block is #++ (meaning it's already wrapped)
|
|
80
|
+
return false if block_start < 1
|
|
81
|
+
|
|
82
|
+
prev_line = lines[block_start - 1]
|
|
83
|
+
prev_line =~ /^#{Regexp.escape(prefix)}#\+\+\s*$/
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def process_file(filepath)
|
|
87
|
+
content = File.read(filepath)
|
|
88
|
+
lines = content.lines
|
|
89
|
+
|
|
90
|
+
blocks = find_rdoc_code_blocks(lines)
|
|
91
|
+
|
|
92
|
+
# Filter out already-wrapped blocks
|
|
93
|
+
blocks.reject! { |b| is_already_wrapped?(lines, b[:start], b[:prefix]) }
|
|
94
|
+
|
|
95
|
+
return if blocks.empty?
|
|
96
|
+
|
|
97
|
+
# Apply changes in reverse order to preserve line numbers
|
|
98
|
+
blocks.sort_by { |b| -b[:start] }.each do |block|
|
|
99
|
+
year = get_latest_git_year(filepath, block[:start] + 1, block[:end] + 1)
|
|
100
|
+
prefix = block[:prefix]
|
|
101
|
+
|
|
102
|
+
# Build the wrapper lines
|
|
103
|
+
# REUSE-IgnoreStart
|
|
104
|
+
begin_wrapper = [
|
|
105
|
+
"#{prefix}#--\n",
|
|
106
|
+
"#{prefix}# SPDX-SnippetBegin\n",
|
|
107
|
+
"#{prefix}# SPDX-FileCopyrightText: #{year} #{COPYRIGHT_HOLDER}\n",
|
|
108
|
+
"#{prefix}# SPDX-License-Identifier: #{LICENSE}\n",
|
|
109
|
+
"#{prefix}#++\n",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
end_wrapper = [
|
|
113
|
+
"#{prefix}#--\n",
|
|
114
|
+
"#{prefix}# SPDX-SnippetEnd\n",
|
|
115
|
+
"#{prefix}#++\n",
|
|
116
|
+
]
|
|
117
|
+
# REUSE-IgnoreEnd
|
|
118
|
+
|
|
119
|
+
# Insert end wrapper after the block
|
|
120
|
+
lines.insert(block[:end] + 1, *end_wrapper)
|
|
121
|
+
# Insert begin wrapper before the block
|
|
122
|
+
lines.insert(block[:start], *begin_wrapper)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
File.write(filepath, lines.join)
|
|
126
|
+
puts "Updated: #{filepath} (#{blocks.length} code block(s))"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def find_rb_files(paths)
|
|
130
|
+
if paths.empty?
|
|
131
|
+
`git ls-files '*.rb'`.split("\n")
|
|
132
|
+
else
|
|
133
|
+
paths.flat_map do |path|
|
|
134
|
+
if File.directory?(path)
|
|
135
|
+
`git ls-files '#{path}/**/*.rb'`.split("\n")
|
|
136
|
+
else
|
|
137
|
+
path
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
if __FILE__ == $0
|
|
144
|
+
paths = ARGV.empty? ? [] : ARGV
|
|
145
|
+
files = find_rb_files(paths)
|
|
146
|
+
|
|
147
|
+
files.each do |file|
|
|
148
|
+
process_file(file)
|
|
149
|
+
end
|
|
150
|
+
end
|
data/tasks/license.rake
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#--
|
|
4
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
+
#++
|
|
7
|
+
|
|
8
|
+
namespace :license do
|
|
9
|
+
namespace :headers do
|
|
10
|
+
desc "Ensure markdown files have correct CC-BY-SA-4.0 headers"
|
|
11
|
+
task :md, [:files] do |_t, args|
|
|
12
|
+
files = args[:files] || ""
|
|
13
|
+
ruby "tasks/license/headers_md.rb #{files}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
desc "Ensure Ruby files have correct AGPL-3.0-or-later headers"
|
|
17
|
+
task :rb, [:files] do |_t, args|
|
|
18
|
+
files = args[:files] || ""
|
|
19
|
+
ruby "tasks/license/headers_rb.rb #{files}"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc "Ensure all files have correct license headers"
|
|
23
|
+
task :all do
|
|
24
|
+
Rake::Task["license:headers:md"].invoke
|
|
25
|
+
Rake::Task["license:headers:rb"].invoke
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
namespace :snippets do
|
|
30
|
+
desc "Add MIT-0 SPDX snippet headers to markdown fenced code blocks"
|
|
31
|
+
task :md, [:files] do |_t, args|
|
|
32
|
+
files = args[:files] || ""
|
|
33
|
+
ruby "tasks/license/snippets_md.rb #{files}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
desc "Add MIT-0 SPDX snippet headers to RDoc code examples in Ruby files"
|
|
37
|
+
task :rdoc, [:files] do |_t, args|
|
|
38
|
+
files = args[:files] || "lib/"
|
|
39
|
+
ruby "tasks/license/snippets_rdoc.rb #{files}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
desc "Add MIT-0 SPDX snippet headers to all code examples"
|
|
43
|
+
task :all do
|
|
44
|
+
Rake::Task["license:snippets:md"].invoke
|
|
45
|
+
Rake::Task["license:snippets:rdoc"].invoke
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
desc "Run all license tasks (headers + snippets)"
|
|
50
|
+
task all: ["headers:all", "snippets:all"]
|
|
51
|
+
|
|
52
|
+
desc "Run license tasks on changed files only (staged + unstaged)"
|
|
53
|
+
task :new do
|
|
54
|
+
# Get changed .md and .rb files (staged and unstaged)
|
|
55
|
+
changed_md = `git diff --name-only --diff-filter=ACMR HEAD -- '*.md' 2>/dev/null`.split("\n")
|
|
56
|
+
staged_md = `git diff --name-only --cached --diff-filter=ACMR -- '*.md' 2>/dev/null`.split("\n")
|
|
57
|
+
changed_rb = `git diff --name-only --diff-filter=ACMR HEAD -- '*.rb' 2>/dev/null`.split("\n")
|
|
58
|
+
staged_rb = `git diff --name-only --cached --diff-filter=ACMR -- '*.rb' 2>/dev/null`.split("\n")
|
|
59
|
+
|
|
60
|
+
# Also get untracked new files
|
|
61
|
+
untracked = `git ls-files --others --exclude-standard`.split("\n")
|
|
62
|
+
untracked_md = untracked.select { |f| f.end_with?(".md") }
|
|
63
|
+
untracked_rb = untracked.select { |f| f.end_with?(".rb") }
|
|
64
|
+
|
|
65
|
+
md_files = (changed_md + staged_md + untracked_md).uniq.join(" ")
|
|
66
|
+
rb_files = (changed_rb + staged_rb + untracked_rb).uniq
|
|
67
|
+
|
|
68
|
+
# Filter rb files to only lib/
|
|
69
|
+
lib_rb_files = rb_files.select { |f| f.start_with?("lib/") }.join(" ")
|
|
70
|
+
|
|
71
|
+
if md_files.empty? && lib_rb_files.empty?
|
|
72
|
+
puts "No changed .md or lib/*.rb files to process"
|
|
73
|
+
else
|
|
74
|
+
unless md_files.empty?
|
|
75
|
+
puts "Processing #{md_files.split.count} changed .md file(s)..."
|
|
76
|
+
Rake::Task["license:headers:md"].invoke(md_files)
|
|
77
|
+
Rake::Task["license:headers:md"].reenable
|
|
78
|
+
Rake::Task["license:snippets:md"].invoke(md_files)
|
|
79
|
+
Rake::Task["license:snippets:md"].reenable
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
unless lib_rb_files.empty?
|
|
83
|
+
puts "Processing #{lib_rb_files.split.count} changed lib/*.rb file(s)..."
|
|
84
|
+
Rake::Task["license:headers:rb"].invoke(lib_rb_files)
|
|
85
|
+
Rake::Task["license:headers:rb"].reenable
|
|
86
|
+
Rake::Task["license:snippets:rdoc"].invoke(lib_rb_files)
|
|
87
|
+
Rake::Task["license:snippets:rdoc"].reenable
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/tasks/rdoc_config.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText:
|
|
1
|
+
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
2
2
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
3
3
|
|
|
4
4
|
image: archlinux
|
|
@@ -13,12 +13,14 @@ packages:
|
|
|
13
13
|
- gdbm
|
|
14
14
|
- ncurses
|
|
15
15
|
- libffi
|
|
16
|
+
<%- if has_rust -%>
|
|
16
17
|
- clang
|
|
18
|
+
<%- end -%>
|
|
17
19
|
- git
|
|
18
20
|
artifacts:
|
|
19
|
-
-
|
|
21
|
+
- <%= gem_name %>/pkg/<%= gem_filename %>
|
|
20
22
|
sources:
|
|
21
|
-
- https://git.sr.ht/~kerrick
|
|
23
|
+
- https://git.sr.ht/~kerrick/<%= gem_name %>
|
|
22
24
|
tasks:
|
|
23
25
|
- setup: |
|
|
24
26
|
curl https://mise.jdx.dev/install.sh | sh
|
|
@@ -26,10 +28,12 @@ tasks:
|
|
|
26
28
|
echo 'eval "$($HOME/.local/bin/mise activate bash)"' >> ~/.buildenv
|
|
27
29
|
echo 'export LANG="en_US.UTF-8"' >> ~/.buildenv
|
|
28
30
|
echo 'export LC_ALL="en_US.UTF-8"' >> ~/.buildenv
|
|
31
|
+
<%- if has_rust -%>
|
|
29
32
|
echo 'export BINDGEN_EXTRA_CLANG_ARGS="-include stdbool.h"' >> ~/.buildenv
|
|
33
|
+
<%- end -%>
|
|
30
34
|
. ~/.buildenv
|
|
31
35
|
export CI="true"
|
|
32
|
-
cd
|
|
36
|
+
cd <%= gem_name %>
|
|
33
37
|
sed -i 's/ruby = .*/ruby = "<%= ruby_version %>"/' mise.toml
|
|
34
38
|
mise install
|
|
35
39
|
mise x -- pip install reuse
|
|
@@ -37,18 +41,20 @@ tasks:
|
|
|
37
41
|
mise reshim
|
|
38
42
|
mise x -- bundle config set --local frozen 'true'
|
|
39
43
|
mise x -- bundle install
|
|
44
|
+
<%- if has_rust -%>
|
|
40
45
|
mise x -- bundle exec rake compile
|
|
46
|
+
<%- end -%>
|
|
41
47
|
- test: |
|
|
42
48
|
. ~/.buildenv
|
|
43
|
-
cd
|
|
49
|
+
cd <%= gem_name %>
|
|
44
50
|
echo "Testing Ruby <%= ruby_version %>"
|
|
45
51
|
mise x -- bundle exec rake test
|
|
46
52
|
- lint: |
|
|
47
53
|
. ~/.buildenv
|
|
48
|
-
cd
|
|
54
|
+
cd <%= gem_name %>
|
|
49
55
|
echo "Linting Ruby <%= ruby_version %>"
|
|
50
56
|
mise x -- bundle exec rake lint
|
|
51
57
|
- package: |
|
|
52
58
|
. ~/.buildenv
|
|
53
|
-
cd
|
|
59
|
+
cd <%= gem_name %>
|
|
54
60
|
mise x -- bundle exec rake build
|
data/tasks/sourcehut.rake
CHANGED
|
@@ -42,7 +42,9 @@ namespace :sourcehut do
|
|
|
42
42
|
rubies.each do |ruby_version|
|
|
43
43
|
filename = ".builds/ruby-#{ruby_version}.yml"
|
|
44
44
|
puts "Generating #{filename}..."
|
|
45
|
-
|
|
45
|
+
gem_name = spec.name
|
|
46
|
+
has_rust = File.exist?("ext/#{gem_name}/Cargo.toml")
|
|
47
|
+
content = erb.result_with_hash(ruby_version:, gem_name:, gem_filename:, bundler_version:, has_rust:)
|
|
46
48
|
File.write(filename, content)
|
|
47
49
|
end
|
|
48
50
|
end
|