scriptor 0.0.1 → 0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3113e6f5f9a12c71ac148fa619294526176874ea8bd73ccefd6e32cee7b7ab5
4
- data.tar.gz: 2997de1697ede585d1d536659438cb4a36932ef313e0c1b52c0cc720efb09be3
3
+ metadata.gz: 9cbfe438c0b163aed13ee3f067785c77719f9d4dc8632c7054e9e249722b530c
4
+ data.tar.gz: 24112730551af6d90ef3c0f4ac3fe557a3d4e0adece1913a49fb7663eb892e99
5
5
  SHA512:
6
- metadata.gz: 648e0d5a14e4843982955278b7f542c84284d96fed4b8194f34c374b80e077566998ba015448f15ad4648845103902ece266248d35b9973144f235015d7b6f56
7
- data.tar.gz: b47e844e4c220aa288bd371ed030f304716ef6e8ce52bea2789c5836185ffbf59868191fa5b7cfc5a5109d66b8f389b077e56f65f2dea8a0e4c3692d34d03b8b
6
+ metadata.gz: 74bc62088328ecf150c2cb8c02617167aa7392576edb955eea7f98785e6b9467082ab861433edb99e72fdc6ac92240f7211f67b0aab394a2348a62be82fc5e82
7
+ data.tar.gz: a861ce4689eadf74a6d9c814bb1a1691401953c60207ac824e64d8497f1c335dcdae2c1c1aa929bc559306db3bb2355266b3aa38e7c151da9b4ecde59ae44e69
data/README.md CHANGED
@@ -1,28 +1,42 @@
1
1
  # Scriptor
2
- Short description and motivation.
3
2
 
4
- ## Usage
5
- How to use my plugin.
3
+ Scriptorはscriptフォルダにあるrubyファイルを、ブラウザから実行できるようにしたRails Engineです。
4
+ `Rails 8.0.0 >= 0` の場合 `bin/rails generate scriot my_script` を実行すると `script/myscript.rb` が作成されます。
5
+ (Rails 8.0.0 以上でなくても、scriptフォルダにファイルを置いておけば利用可能です。)
6
6
 
7
- ## Installation
8
- Add this line to your application's Gemfile:
7
+ このscriptをブラウザから一覧で見たり、詳細ページではコードを閲覧したり、実行できます。
9
8
 
10
- ```ruby
11
- gem "scriptor"
12
- ```
9
+ https://github.com/user-attachments/assets/057c75ce-7c8e-4c89-a238-13546b5797f1
13
10
 
14
- And then execute:
15
- ```bash
16
- $ bundle
17
- ```
18
11
 
19
- Or install it yourself as:
20
- ```bash
21
- $ gem install scriptor
22
- ```
12
+
13
+ ## 使い方
14
+
15
+ 1. `bundle add scriptor`
16
+ 2. `bin/rails generate maintenance_tasks:install` を実行
17
+ - `config/routes.rb` に `mount Scriptor::Engine => "/scriptor"` が記載されます
18
+ - `db/migrations/****_create_scriptor_executions.scriptor.rb` ファイルが作成されます
19
+ 3. scriptフォルダにスクリプトを作ると、/scriptor ページにスクリプト一覧が並びます
20
+
21
+ で完成です。
22
+
23
+ ## リリース手順
24
+
25
+ 0. version.rb と Gemfile.lockのscriptorのバージョンが適切か確認しましょう
26
+
27
+
28
+ 1. gemのbuild
29
+ ```shell
30
+ gem build scriptor.gemspec
31
+ ```
32
+
33
+ 2. gemのpush
34
+ ```shll
35
+ gem push scriptor-*.*.*.gem
36
+ ```
23
37
 
24
38
  ## Contributing
25
- Contribution directions go here.
39
+ - 色々足りていません。大歓迎です。
26
40
 
27
41
  ## License
28
42
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -6,14 +6,16 @@ module Scriptor
6
6
 
7
7
  def show
8
8
  @script = Scriptor::Script.find(params[:filename])
9
+ @executions = Execution.where(script_filename: @script.filename).order(id: :desc)
9
10
  end
10
11
 
11
12
  def run
12
13
  script = Scriptor::Script.find(params[:filename])
13
14
  args = params[:args].to_s.strip.split
14
15
  script.run(*args)
15
-
16
- redirect_to script_path(script.filename, notice: "Script executed successfully.")
16
+ redirect_to script_path(script.filename), notice: "Script executed successfully."
17
+ rescue StandardError => e
18
+ redirect_to script_path(script.filename), alert: "Error executing script: #{e.message}"
17
19
  end
18
20
  end
19
21
  end
@@ -0,0 +1,18 @@
1
+ module Scriptor
2
+ class Execution < ApplicationRecord
3
+ validates :script_filename, presence: true
4
+ validates :executed_command, presence: true
5
+ validates :started_at, presence: true
6
+
7
+ enum :status, { running: "running", success: "success", error: "error" }
8
+
9
+ #
10
+ # Rails.rootパスを.に置換。想定として、"ruby /Users/hatsu/..."のように
11
+ # ruby [space]の直後にRails.root.to_sが現れることを想定しているため、
12
+ # /^ruby\s+#{Rails.root}/ のような正規表現で置き換える。
13
+ #
14
+ def truncated_command
15
+ executed_command.sub(/^ruby\s+#{Regexp.escape(Rails.root.to_s)}/, "ruby .")
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,5 @@
1
+ require "open3"
2
+
1
3
  module Scriptor
2
4
  class Script
3
5
  attr_reader :filename, :content
@@ -31,14 +33,26 @@ module Scriptor
31
33
  script_path = Rails.root.join("script", "#{filename}.rb")
32
34
  raise "Script file does not exist: #{script_path}" unless File.exist?(script_path)
33
35
 
36
+ escaped_args = args.map(&:shellescape).join(" ")
37
+ command = "ruby #{script_path} #{escaped_args}"
34
38
  # Rails 環境が正しくロードされるように Rails.root をカレントディレクトリに設定
39
+ execution = Execution.create!(
40
+ script_filename: filename,
41
+ status: :running,
42
+ executed_command: command,
43
+ started_at: Time.current
44
+ )
35
45
  Dir.chdir(Rails.root) do
36
- escaped_args = args.map(&:shellescape).join(" ")
37
- system("ruby #{script_path} #{escaped_args}")
46
+ _, err, status = Open3.capture3(command)
47
+ if status.success?
48
+ # コマンド成功時の処理
49
+ execution.update!(status: :success, finished_at: Time.current)
50
+ else
51
+ error_message = err.presence || "Command failed with exit status #{status.exitstatus}"
52
+ execution.update!(status: :error, error_message: error_message.strip, finished_at: Time.current)
53
+ raise error_message
54
+ end
38
55
  end
39
- rescue StandardError => e
40
- Rails.logger.error "Error running script #{filename}: #{e.message}"
41
- raise e
42
56
  end
43
57
 
44
58
  private
@@ -7,12 +7,55 @@
7
7
 
8
8
  <%= yield :head %>
9
9
 
10
- <%= stylesheet_link_tag "scriptor/application", media: "all" %>
10
+ <%= stylesheet_link_tag "scriptor/application", media: "all" %>
11
11
  <script src="https://cdn.tailwindcss.com"></script>
12
12
  </head>
13
- <body>
14
-
15
- <%= yield %>
13
+ <body class="bg-gray-900 text-gray-200">
14
+ <!-- Header -->
15
+ <header class="bg-gray-800 border-b border-gray-700">
16
+ <div class="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
17
+ <!-- Left: Title -->
18
+ <h1 class="text-xl font-bold text-blue-400">
19
+ <%= link_to "Scriptor", root_path, class: "hover:underline" %>
20
+ </h1>
21
+ <!-- Right: GitHub link -->
22
+ <a href="https://github.com/hatsu38/scriptor" target="_blank" rel="noopener noreferrer"
23
+ class="flex items-center space-x-2 text-gray-300 hover:text-gray-100 transition">
24
+ <svg class="h-5 w-5" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
25
+ <path fill-rule="evenodd" clip-rule="evenodd" d="M12 .296C5.372.296 0 5.668 0 12.296c0 5.3 3.438 9.773 8.205 11.368.6.111.82-.258.82-.578
26
+ 0-.285-.01-1.04-.016-2.039-3.338.726-4.042-1.61-4.042-1.61-.545-1.385-1.333-1.755-1.333-1.755-1.091-.747.083-.732.083-.732
27
+ 1.205.084 1.84 1.238 1.84 1.238 1.07 1.835 2.807 1.305 3.493.998.108-.776.418-1.305.76-1.606-2.665-.303-5.466-1.333-5.466-5.933
28
+ 0-1.311.469-2.382 1.237-3.223-.124-.304-.536-1.523.118-3.176 0 0 1.01-.323 3.301 1.23.96-.267 1.98-.399 3-.404 1.02.005
29
+ 2.04.137 3 .404 2.291-1.553 3.3-1.23 3.3-1.23.655 1.653.243 2.872.12 3.176.77.841 1.237 1.912 1.237 3.223
30
+ 0 4.61-2.804 5.626-5.476 5.922.43.372.81 1.102.81 2.222 0 1.606-.014 2.903-.014 3.301 0 .321.22.694.825.576C20.565 22.068
31
+ 24 17.595 24 12.296c0-6.628-5.372-12-12-12z"/>
32
+ </svg>
33
+ <span class="font-semibold">GitHub</span>
34
+ </a>
35
+ </div>
36
+ </header>
16
37
 
38
+ <section class="max-w-7xl mx-auto px-4 py-6">
39
+ <% if notice %>
40
+ <div class="mb-4 flex items-center space-x-3 bg-green-800 border border-green-500 rounded-md p-4 shadow-lg">
41
+ <svg class="h-5 w-5 text-green-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"
42
+ xmlns="http://www.w3.org/2000/svg">
43
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
44
+ d="M9 11l3 3L22 4"></path>
45
+ </svg>
46
+ <span class="font-semibold"><%= notice %></span>
47
+ </div>
48
+ <% elsif alert %>
49
+ <div class="mb-4 flex items-center space-x-3 bg-red-800 border border-red-500 rounded-md p-4 shadow-lg">
50
+ <svg class="h-5 w-5 text-red-400 flex-shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24"
51
+ xmlns="http://www.w3.org/2000/svg">
52
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
53
+ d="M18.364 5.636L12 12m0 0L5.636 5.636M12 12l0 8"></path>
54
+ </svg>
55
+ <span class="font-semibold"><%= alert %></span>
56
+ </div>
57
+ <% end %>
58
+ <%= yield %>
59
+ </section>
17
60
  </body>
18
61
  </html>
@@ -1,5 +1,5 @@
1
1
  <div class="min-h-screen bg-gray-900 text-gray-200 py-8">
2
- <div class="max-w-4xl mx-auto px-4">
2
+ <div class="max-w-7xl mx-auto px-4">
3
3
  <!-- Header -->
4
4
  <h1 class="text-4xl font-bold text-blue-400 mb-8 border-b border-gray-700 pb-2">
5
5
  📜 Scripts Explorer
@@ -10,7 +10,12 @@
10
10
  <% @scripts.each_with_index do |script, index| %>
11
11
  <div class="bg-gray-800 hover:bg-gray-700 p-4 rounded-lg shadow-lg transition duration-200 group">
12
12
  <h2 class="text-xl font-semibold mb-2 text-gray-300 group-hover:text-blue-300">
13
- <%= index + 1 %>. <%= link_to script.filename, script_path(script.filename), class: "hover:underline" %>
13
+ <!-- 親要素か、直接テキストを含む要素にtruncateクラスを適用 -->
14
+ <!-- inline-blockとmax-w-fullを組み合わせることで、最大幅内で省略可能に -->
15
+ <span class="inline-block max-w-full truncate">
16
+ <%= index + 1 %>.
17
+ <%= link_to script.filename, script_path(script.filename), class: "hover:underline" %>
18
+ </span>
14
19
  </h2>
15
20
  </div>
16
21
  <% end %>
@@ -1,5 +1,5 @@
1
1
  <div class="min-h-screen bg-gray-900 text-gray-200 py-8">
2
- <div class="max-w-4xl mx-auto px-4">
2
+ <div class="max-w-7xl mx-auto px-4">
3
3
  <!-- Header -->
4
4
  <h1 class="text-4xl font-bold text-blue-400 mb-8 border-b border-gray-700 pb-2">
5
5
  Script Details
@@ -43,6 +43,60 @@
43
43
  <% end %>
44
44
  </div>
45
45
 
46
+ <!-- Execution History -->
47
+ <% if @executions.present? %>
48
+ <div class="bg-gray-800 shadow-lg rounded-lg p-6 mb-6">
49
+ <!-- タイトル部分をクリックすると開閉 -->
50
+ <h3 id="execution-history-toggle" class="text-xl font-semibold text-blue-300 mb-4 cursor-pointer flex items-center justify-between select-none">
51
+ <span>Execution History</span>
52
+ <svg id="toggle-icon" class="h-5 w-5 text-gray-400 transform transition-transform duration-200" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
53
+ <!-- 折り畳みアイコン(初期は閉じた状態に見えるように) -->
54
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" />
55
+ </svg>
56
+ </h3>
57
+
58
+ <!-- 初期状態ではhidden -->
59
+ <div id="execution-history-content" class="space-y-4 hidden">
60
+ <% @executions.each do |execution| %>
61
+ <div class="bg-gray-700 rounded-lg p-4 shadow-md flex flex-col sm:flex-row sm:justify-between sm:items-start space-y-4 sm:space-y-0 sm:space-x-4">
62
+ <div class="flex-1">
63
+ <div class="mb-6 text-gray-100 font-semibold">
64
+ Status:
65
+ <% if execution.status == "success" %>
66
+ <span class="text-green-400"><%= execution.status.capitalize %></span>
67
+ <% else %>
68
+ <span class="text-red-400"><%= execution.status.capitalize %></span>
69
+ <% end %>
70
+ </div>
71
+ <div class="text-sm text-gray-300 mt-1 break-words">
72
+ <span class="text-gray-400 font-medium">Command:</span>
73
+ <span class="font-mono text-gray-200 max-h-24 overflow-auto">
74
+ <%= execution.truncated_command %>
75
+ </span>
76
+ </div>
77
+ <% if execution.error_message.present? %>
78
+ <div class="text-sm text-red-400 mt-2 break-words">
79
+ <span class="text-gray-400 font-medium">Error:</span>
80
+ <div class="font-mono whitespace-pre-wrap break-words">
81
+ <%= execution.error_message %>
82
+ </div>
83
+ </div>
84
+ <% end %>
85
+ </div>
86
+ <div class="text-right text-sm text-gray-400">
87
+ <div>Started: <%= execution.started_at.strftime("%Y-%m-%d %H:%M:%S") %></div>
88
+ <% if execution.finished_at %>
89
+ <div>Finished: <%= execution.finished_at.strftime("%Y-%m-%d %H:%M:%S") %></div>
90
+ <% else %>
91
+ <div>Running...</div>
92
+ <% end %>
93
+ </div>
94
+ </div>
95
+ <% end %>
96
+ </div>
97
+ </div>
98
+ <% end %>
99
+
46
100
  <!-- Back Link -->
47
101
  <div class="flex justify-end">
48
102
  <%= link_to '⬅️ Back to Index', scripts_path, class: "text-blue-500 hover:text-blue-400 font-medium hover:underline transition duration-200" %>
@@ -71,4 +125,22 @@
71
125
  const args = argsInput.value.trim();
72
126
  commandPreviewArgs.textContent = args;
73
127
  });
128
+
129
+ // Toggle execution history
130
+ const toggleHeading = document.getElementById('execution-history-toggle');
131
+ const content = document.getElementById('execution-history-content');
132
+ const toggleIcon = document.getElementById('toggle-icon');
133
+
134
+ if (toggleHeading && content) {
135
+ toggleHeading.addEventListener('click', () => {
136
+ content.classList.toggle('hidden');
137
+ // アイコンの向きを切り替え
138
+ // 開いている場合は上向き矢印、閉じている場合は下向き矢印に
139
+ if (content.classList.contains('hidden')) {
140
+ toggleIcon.classList.remove('rotate-180');
141
+ } else {
142
+ toggleIcon.classList.add('rotate-180');
143
+ }
144
+ });
145
+ }
74
146
  </script>
@@ -0,0 +1,14 @@
1
+ class CreateScriptorExecutions < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :scriptor_executions do |t|
4
+ t.string :script_filename, null: false, index: true
5
+ t.string :status, null: false
6
+ t.text :executed_command, null: false
7
+ t.text :error_message, null: true
8
+ t.datetime :started_at, null: false
9
+ t.datetime :finished_at, null: true
10
+
11
+ t.timestamps
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ module Scriptor
2
+ class InstallGenerator < Rails::Generators::Base
3
+ desc "Install Scriptor"
4
+
5
+ # Mounts the engine in the host application's config/routes.rb
6
+ def mount_engine
7
+ route("mount Scriptor::Engine, at: \"/scriptor\"")
8
+ end
9
+
10
+ # Copies engine migrations to host application and migrates the database
11
+ def install_migrations
12
+ rake("scriptor:install:migrations")
13
+ rake("db:migrate")
14
+ end
15
+ end
16
+ end
@@ -1,3 +1,3 @@
1
1
  module Scriptor
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.1.0".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scriptor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - hatsu38
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-12-18 00:00:00.000000000 Z
11
+ date: 2024-12-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -51,11 +51,14 @@ files:
51
51
  - app/jobs/scriptor/application_job.rb
52
52
  - app/mailers/scriptor/application_mailer.rb
53
53
  - app/models/scriptor/application_record.rb
54
+ - app/models/scriptor/execution.rb
54
55
  - app/models/scriptor/script.rb
55
56
  - app/views/layouts/scriptor/application.html.erb
56
57
  - app/views/scriptor/scripts/index.html.erb
57
58
  - app/views/scriptor/scripts/show.html.erb
58
59
  - config/routes.rb
60
+ - db/migrate/20241219110922_create_scriptor_executions.rb
61
+ - lib/generators/scriptor/install_generator.rb
59
62
  - lib/scriptor.rb
60
63
  - lib/scriptor/engine.rb
61
64
  - lib/scriptor/version.rb