ocak 0.1.0 → 0.2.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/lib/ocak/agent_generator.rb +4 -2
- data/lib/ocak/cli.rb +2 -0
- data/lib/ocak/commands/hiz.rb +180 -0
- data/lib/ocak/commands/resume.rb +3 -3
- data/lib/ocak/issue_fetcher.rb +6 -3
- data/lib/ocak/monorepo_detector.rb +97 -0
- data/lib/ocak/pipeline_executor.rb +176 -0
- data/lib/ocak/pipeline_runner.rb +10 -201
- data/lib/ocak/pipeline_state.rb +4 -2
- data/lib/ocak/process_runner.rb +4 -2
- data/lib/ocak/stack_detector.rb +149 -281
- data/lib/ocak/stream_parser.rb +2 -1
- data/lib/ocak.rb +1 -1
- metadata +4 -1
data/lib/ocak/stack_detector.rb
CHANGED
|
@@ -1,12 +1,137 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'json'
|
|
4
|
+
require_relative 'monorepo_detector'
|
|
4
5
|
|
|
5
6
|
module Ocak
|
|
6
7
|
class StackDetector
|
|
8
|
+
include MonorepoDetector
|
|
9
|
+
|
|
7
10
|
Result = Struct.new(:language, :framework, :test_command, :lint_command,
|
|
8
11
|
:format_command, :security_commands, :setup_command,
|
|
9
12
|
:monorepo, :packages)
|
|
13
|
+
LANGUAGE_RULES = [
|
|
14
|
+
['ruby', ['Gemfile']],
|
|
15
|
+
['typescript', ['tsconfig.json']],
|
|
16
|
+
['javascript', ['package.json']],
|
|
17
|
+
['python', ['pyproject.toml', 'setup.py', 'requirements.txt']],
|
|
18
|
+
['rust', ['Cargo.toml']],
|
|
19
|
+
['go', ['go.mod']],
|
|
20
|
+
['java', ['pom.xml', 'build.gradle']],
|
|
21
|
+
['elixir', ['mix.exs']]
|
|
22
|
+
].freeze
|
|
23
|
+
FRAMEWORK_RULES = {
|
|
24
|
+
'ruby' => [
|
|
25
|
+
[:dep_in_file, 'Gemfile', 'rails', 'rails'],
|
|
26
|
+
[:dep_in_file, 'Gemfile', 'sinatra', 'sinatra'],
|
|
27
|
+
[:dep_in_file, 'Gemfile', 'hanami', 'hanami']
|
|
28
|
+
],
|
|
29
|
+
'javascript' => [
|
|
30
|
+
[:pkg_has, 'next', 'next'], [:pkg_has, '@remix-run/react', 'remix'],
|
|
31
|
+
[:pkg_has, 'nuxt', 'nuxt'], [:pkg_has, 'svelte', 'svelte'],
|
|
32
|
+
[:pkg_has, '@sveltejs/kit', 'svelte'], [:pkg_has, 'react', 'react'],
|
|
33
|
+
[:pkg_has, 'vue', 'vue'], [:pkg_has, 'express', 'express']
|
|
34
|
+
],
|
|
35
|
+
'python' => [
|
|
36
|
+
[:file_exists, 'manage.py', 'django'], [:pip_has, 'django', 'django'],
|
|
37
|
+
[:pip_has, 'flask', 'flask'], [:pip_has, 'fastapi', 'fastapi']
|
|
38
|
+
],
|
|
39
|
+
'rust' => [
|
|
40
|
+
[:file_contains, 'Cargo.toml', 'actix-web', 'actix'],
|
|
41
|
+
[:file_contains, 'Cargo.toml', 'axum', 'axum'],
|
|
42
|
+
[:file_contains, 'Cargo.toml', 'rocket', 'rocket']
|
|
43
|
+
],
|
|
44
|
+
'go' => [
|
|
45
|
+
[:file_contains, 'go.mod', 'gin-gonic', 'gin'],
|
|
46
|
+
[:file_contains, 'go.mod', 'labstack/echo', 'echo'],
|
|
47
|
+
[:file_contains, 'go.mod', 'gofiber/fiber', 'fiber'],
|
|
48
|
+
[:file_contains, 'go.mod', 'go-chi/chi', 'chi']
|
|
49
|
+
],
|
|
50
|
+
'elixir' => [[:dep_in_file, 'mix.exs', 'phoenix', 'phoenix']]
|
|
51
|
+
}.freeze
|
|
52
|
+
TOOL_RULES = {
|
|
53
|
+
test: {
|
|
54
|
+
'ruby' => [
|
|
55
|
+
[:dep_in_file, 'Gemfile', 'rspec', 'bundle exec rspec'],
|
|
56
|
+
[:always, 'bundle exec rake test']
|
|
57
|
+
],
|
|
58
|
+
'javascript' => [
|
|
59
|
+
[:pkg_has, 'vitest', 'npx vitest run'],
|
|
60
|
+
[:pkg_has, 'jest', 'npx jest'],
|
|
61
|
+
[:always, 'npm test']
|
|
62
|
+
],
|
|
63
|
+
'python' => [
|
|
64
|
+
[:file_contains, 'pyproject.toml', 'pytest', 'pytest'],
|
|
65
|
+
[:always, 'python -m pytest']
|
|
66
|
+
],
|
|
67
|
+
'rust' => [[:always, 'cargo test']],
|
|
68
|
+
'go' => [[:always, 'go test ./...']],
|
|
69
|
+
'java' => [[:file_exists, 'gradlew', './gradlew test'], [:always, 'mvn test']],
|
|
70
|
+
'elixir' => [[:always, 'mix test']]
|
|
71
|
+
},
|
|
72
|
+
lint: {
|
|
73
|
+
'ruby' => [[:dep_in_file, 'Gemfile', 'rubocop', 'bundle exec rubocop -A']],
|
|
74
|
+
'javascript' => [
|
|
75
|
+
[:pkg_has, 'biome', 'npx biome check --write'],
|
|
76
|
+
[:pkg_has, '@biomejs/biome', 'npx biome check --write'],
|
|
77
|
+
[:pkg_has, 'eslint', 'npx eslint --fix .']
|
|
78
|
+
],
|
|
79
|
+
'python' => [
|
|
80
|
+
[:file_contains, 'pyproject.toml', 'ruff', 'ruff check --fix .'],
|
|
81
|
+
[:always, 'flake8']
|
|
82
|
+
],
|
|
83
|
+
'rust' => [[:always, 'cargo clippy --fix --allow-dirty']],
|
|
84
|
+
'go' => [[:always, 'golangci-lint run']],
|
|
85
|
+
'elixir' => [[:always, 'mix credo']]
|
|
86
|
+
},
|
|
87
|
+
format: {
|
|
88
|
+
'javascript' => [
|
|
89
|
+
[:pkg_has, 'biome', nil],
|
|
90
|
+
[:pkg_has, '@biomejs/biome', nil],
|
|
91
|
+
[:pkg_has, 'prettier', 'npx prettier --write .']
|
|
92
|
+
],
|
|
93
|
+
'python' => [
|
|
94
|
+
[:file_contains, 'pyproject.toml', 'ruff', 'ruff format .'],
|
|
95
|
+
[:file_contains, 'pyproject.toml', 'black', 'black .']
|
|
96
|
+
],
|
|
97
|
+
'rust' => [[:always, 'cargo fmt']],
|
|
98
|
+
'go' => [[:always, 'gofmt -w .']],
|
|
99
|
+
'elixir' => [[:always, 'mix format']]
|
|
100
|
+
},
|
|
101
|
+
security: {
|
|
102
|
+
'ruby' => [
|
|
103
|
+
[:dep_in_file, 'Gemfile', 'brakeman', 'bundle exec brakeman -q'],
|
|
104
|
+
[:dep_in_file, 'Gemfile', 'bundler-audit', 'bundle exec bundler-audit check']
|
|
105
|
+
],
|
|
106
|
+
'javascript' => [[:always, 'npm audit --omit=dev']],
|
|
107
|
+
'python' => [
|
|
108
|
+
[:file_contains, 'pyproject.toml', 'bandit', 'bandit -r .'],
|
|
109
|
+
[:file_contains, 'pyproject.toml', 'safety', 'safety check']
|
|
110
|
+
],
|
|
111
|
+
'rust' => [[:file_contains, 'Cargo.toml', 'cargo-audit', 'cargo audit']],
|
|
112
|
+
'go' => [[:always, 'gosec ./...']]
|
|
113
|
+
},
|
|
114
|
+
setup: {
|
|
115
|
+
'ruby' => [[:file_exists, 'Gemfile', 'bundle install']],
|
|
116
|
+
'javascript' => [
|
|
117
|
+
[:file_exists, 'package-lock.json', 'npm install'],
|
|
118
|
+
[:file_exists, 'yarn.lock', 'yarn install'],
|
|
119
|
+
[:file_exists, 'pnpm-lock.yaml', 'pnpm install'],
|
|
120
|
+
[:file_exists, 'package.json', 'npm install']
|
|
121
|
+
],
|
|
122
|
+
'python' => [
|
|
123
|
+
[:file_exists, 'pyproject.toml', 'pip install -e .'],
|
|
124
|
+
[:file_exists, 'requirements.txt', 'pip install -r requirements.txt']
|
|
125
|
+
],
|
|
126
|
+
'rust' => [[:file_exists, 'Cargo.toml', 'cargo fetch']],
|
|
127
|
+
'go' => [[:file_exists, 'go.mod', 'go mod download']],
|
|
128
|
+
'elixir' => [[:file_exists, 'mix.exs', 'mix deps.get']],
|
|
129
|
+
'java' => [
|
|
130
|
+
[:file_exists, 'gradlew', './gradlew dependencies'],
|
|
131
|
+
[:file_exists, 'pom.xml', 'mvn dependency:resolve']
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
}.freeze
|
|
10
135
|
|
|
11
136
|
def initialize(project_dir)
|
|
12
137
|
@dir = project_dir
|
|
@@ -14,15 +139,16 @@ module Ocak
|
|
|
14
139
|
|
|
15
140
|
def detect
|
|
16
141
|
lang = detect_language
|
|
142
|
+
key = rules_key(lang)
|
|
17
143
|
mono = detect_monorepo
|
|
18
144
|
Result.new(
|
|
19
145
|
language: lang,
|
|
20
|
-
framework:
|
|
21
|
-
test_command:
|
|
22
|
-
lint_command:
|
|
23
|
-
format_command:
|
|
24
|
-
security_commands:
|
|
25
|
-
setup_command:
|
|
146
|
+
framework: first_match(FRAMEWORK_RULES[key]),
|
|
147
|
+
test_command: first_match(TOOL_RULES[:test][key]),
|
|
148
|
+
lint_command: first_match(TOOL_RULES[:lint][key]),
|
|
149
|
+
format_command: first_match(TOOL_RULES[:format][key]),
|
|
150
|
+
security_commands: all_matches(TOOL_RULES[:security][key]),
|
|
151
|
+
setup_command: first_match(TOOL_RULES[:setup][key]),
|
|
26
152
|
monorepo: mono[:detected],
|
|
27
153
|
packages: mono[:packages]
|
|
28
154
|
)
|
|
@@ -31,295 +157,37 @@ module Ocak
|
|
|
31
157
|
private
|
|
32
158
|
|
|
33
159
|
def detect_language
|
|
34
|
-
return
|
|
35
|
-
return 'typescript' if exists?('tsconfig.json')
|
|
36
|
-
return 'javascript' if exists?('package.json')
|
|
37
|
-
return 'python' if exists?('pyproject.toml') || exists?('setup.py') || exists?('requirements.txt')
|
|
38
|
-
return 'rust' if exists?('Cargo.toml')
|
|
39
|
-
return 'go' if exists?('go.mod')
|
|
40
|
-
return 'java' if exists?('pom.xml') || exists?('build.gradle')
|
|
41
|
-
return 'elixir' if exists?('mix.exs')
|
|
42
|
-
|
|
160
|
+
LANGUAGE_RULES.each { |lang, files| return lang if files.any? { |f| exists?(f) } }
|
|
43
161
|
'unknown'
|
|
44
162
|
end
|
|
45
163
|
|
|
46
|
-
def
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
when 'typescript', 'javascript' then detect_js_framework
|
|
50
|
-
when 'python' then detect_python_framework
|
|
51
|
-
when 'rust' then detect_rust_framework
|
|
52
|
-
when 'go' then detect_go_framework
|
|
53
|
-
when 'elixir' then 'phoenix' if gemfile_has?('mix.exs', 'phoenix')
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def detect_test_command(lang)
|
|
58
|
-
case lang
|
|
59
|
-
when 'ruby'
|
|
60
|
-
gemfile_has?('Gemfile', 'rspec') ? 'bundle exec rspec' : 'bundle exec rake test'
|
|
61
|
-
when 'typescript', 'javascript'
|
|
62
|
-
return 'npx vitest run' if pkg_has?('vitest')
|
|
63
|
-
return 'npx jest' if pkg_has?('jest')
|
|
64
|
-
|
|
65
|
-
'npm test'
|
|
66
|
-
when 'python'
|
|
67
|
-
return 'pytest' if exists?('pyproject.toml') && read_file('pyproject.toml').include?('pytest')
|
|
68
|
-
|
|
69
|
-
'python -m pytest'
|
|
70
|
-
when 'rust' then 'cargo test'
|
|
71
|
-
when 'go' then 'go test ./...'
|
|
72
|
-
when 'java' then exists?('gradlew') ? './gradlew test' : 'mvn test'
|
|
73
|
-
when 'elixir' then 'mix test'
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def detect_lint_command(lang)
|
|
78
|
-
case lang
|
|
79
|
-
when 'ruby'
|
|
80
|
-
'bundle exec rubocop -A' if gemfile_has?('Gemfile', 'rubocop')
|
|
81
|
-
when 'typescript', 'javascript'
|
|
82
|
-
return 'npx biome check --write' if pkg_has?('biome') || pkg_has?('@biomejs/biome')
|
|
83
|
-
|
|
84
|
-
'npx eslint --fix .' if pkg_has?('eslint')
|
|
85
|
-
when 'python'
|
|
86
|
-
return 'ruff check --fix .' if exists?('pyproject.toml') && read_file('pyproject.toml').include?('ruff')
|
|
87
|
-
|
|
88
|
-
'flake8'
|
|
89
|
-
when 'rust' then 'cargo clippy --fix --allow-dirty'
|
|
90
|
-
when 'go' then 'golangci-lint run'
|
|
91
|
-
when 'elixir' then 'mix credo'
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def detect_format_command(lang)
|
|
96
|
-
case lang
|
|
97
|
-
when 'ruby' then nil # rubocop handles formatting
|
|
98
|
-
when 'typescript', 'javascript'
|
|
99
|
-
return nil if pkg_has?('biome') || pkg_has?('@biomejs/biome') # biome handles both
|
|
100
|
-
|
|
101
|
-
'npx prettier --write .' if pkg_has?('prettier')
|
|
102
|
-
when 'python'
|
|
103
|
-
return 'ruff format .' if exists?('pyproject.toml') && read_file('pyproject.toml').include?('ruff')
|
|
104
|
-
|
|
105
|
-
'black .' if exists?('pyproject.toml') && read_file('pyproject.toml').include?('black')
|
|
106
|
-
when 'rust' then 'cargo fmt'
|
|
107
|
-
when 'go' then 'gofmt -w .'
|
|
108
|
-
when 'elixir' then 'mix format'
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
def detect_security_commands(lang)
|
|
113
|
-
cmds = []
|
|
114
|
-
case lang
|
|
115
|
-
when 'ruby'
|
|
116
|
-
cmds << 'bundle exec brakeman -q' if gemfile_has?('Gemfile', 'brakeman')
|
|
117
|
-
cmds << 'bundle exec bundler-audit check' if gemfile_has?('Gemfile', 'bundler-audit')
|
|
118
|
-
when 'typescript', 'javascript'
|
|
119
|
-
cmds << 'npm audit --omit=dev'
|
|
120
|
-
when 'python'
|
|
121
|
-
cmds << 'bandit -r .' if exists?('pyproject.toml') && read_file('pyproject.toml').include?('bandit')
|
|
122
|
-
cmds << 'safety check' if exists?('pyproject.toml') && read_file('pyproject.toml').include?('safety')
|
|
123
|
-
when 'rust'
|
|
124
|
-
cmds << 'cargo audit' if read_file('Cargo.toml').include?('cargo-audit')
|
|
125
|
-
when 'go'
|
|
126
|
-
cmds << 'gosec ./...'
|
|
127
|
-
end
|
|
128
|
-
cmds
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def detect_setup_command(lang)
|
|
132
|
-
case lang
|
|
133
|
-
when 'ruby' then 'bundle install' if exists?('Gemfile')
|
|
134
|
-
when 'typescript', 'javascript' then detect_js_setup_command
|
|
135
|
-
when 'python' then detect_python_setup_command
|
|
136
|
-
when 'rust' then 'cargo fetch' if exists?('Cargo.toml')
|
|
137
|
-
when 'go' then 'go mod download' if exists?('go.mod')
|
|
138
|
-
when 'elixir' then 'mix deps.get' if exists?('mix.exs')
|
|
139
|
-
when 'java' then detect_java_setup_command
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
def detect_js_setup_command
|
|
144
|
-
return 'npm install' if exists?('package-lock.json')
|
|
145
|
-
return 'yarn install' if exists?('yarn.lock')
|
|
146
|
-
return 'pnpm install' if exists?('pnpm-lock.yaml')
|
|
147
|
-
|
|
148
|
-
'npm install' if exists?('package.json')
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def detect_python_setup_command
|
|
152
|
-
return 'pip install -e .' if exists?('pyproject.toml')
|
|
153
|
-
|
|
154
|
-
'pip install -r requirements.txt' if exists?('requirements.txt')
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def detect_java_setup_command
|
|
158
|
-
return './gradlew dependencies' if exists?('gradlew')
|
|
159
|
-
|
|
160
|
-
'mvn dependency:resolve' if exists?('pom.xml')
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
# Monorepo detection
|
|
164
|
-
|
|
165
|
-
def detect_monorepo
|
|
166
|
-
packages = []
|
|
167
|
-
packages.concat(detect_npm_workspaces)
|
|
168
|
-
packages.concat(detect_pnpm_workspaces)
|
|
169
|
-
packages.concat(detect_cargo_workspaces)
|
|
170
|
-
packages.concat(detect_go_workspaces)
|
|
171
|
-
packages.concat(detect_lerna_packages)
|
|
172
|
-
packages.concat(detect_convention_packages) if packages.empty?
|
|
173
|
-
packages.uniq!
|
|
174
|
-
{ detected: packages.any?, packages: packages }
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
def detect_npm_workspaces
|
|
178
|
-
return [] unless exists?('package.json')
|
|
179
|
-
|
|
180
|
-
pkg = begin
|
|
181
|
-
JSON.parse(read_file('package.json'))
|
|
182
|
-
rescue JSON::ParserError
|
|
183
|
-
{}
|
|
184
|
-
end
|
|
185
|
-
workspaces = pkg['workspaces']
|
|
186
|
-
workspaces = workspaces['packages'] if workspaces.is_a?(Hash)
|
|
187
|
-
return [] unless workspaces.is_a?(Array) && workspaces.any?
|
|
188
|
-
|
|
189
|
-
expand_workspace_globs(workspaces)
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
def detect_pnpm_workspaces
|
|
193
|
-
return [] unless exists?('pnpm-workspace.yaml')
|
|
194
|
-
|
|
195
|
-
content = read_file('pnpm-workspace.yaml')
|
|
196
|
-
globs = content.scan(/^\s*-\s*['"]?([^'"#\n]+)/).flatten.map(&:strip)
|
|
197
|
-
expand_workspace_globs(globs)
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
def detect_cargo_workspaces
|
|
201
|
-
return [] unless exists?('Cargo.toml') && read_file('Cargo.toml').include?('[workspace]')
|
|
202
|
-
|
|
203
|
-
read_file('Cargo.toml').scan(/members\s*=\s*\[(.*?)\]/m).flatten.flat_map do |members|
|
|
204
|
-
globs = members.scan(/"([^"]+)"/).flatten
|
|
205
|
-
expand_workspace_globs(globs)
|
|
206
|
-
end
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
def detect_go_workspaces
|
|
210
|
-
return [] unless exists?('go.work')
|
|
211
|
-
|
|
212
|
-
read_file('go.work').scan(/use\s+(\S+)/).flatten.select do |pkg|
|
|
213
|
-
Dir.exist?(File.join(@dir, pkg))
|
|
214
|
-
end
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
def detect_lerna_packages
|
|
218
|
-
return [] unless exists?('lerna.json')
|
|
164
|
+
def rules_key(lang) = lang == 'typescript' ? 'javascript' : lang
|
|
165
|
+
def first_match(rules) = rules&.find { |rule| match_rule?(rule) }&.last
|
|
166
|
+
def all_matches(rules) = (rules || []).filter_map { |rule| rule.last if match_rule?(rule) }
|
|
219
167
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
168
|
+
def match_rule?(rule)
|
|
169
|
+
case rule.first
|
|
170
|
+
when :dep_in_file then gemfile_has?(rule[1], rule[2])
|
|
171
|
+
when :pkg_has then pkg_has?(rule[1])
|
|
172
|
+
when :pip_has then pip_has?(rule[1])
|
|
173
|
+
when :file_contains then read_file(rule[1]).include?(rule[2])
|
|
174
|
+
when :file_exists then exists?(rule[1])
|
|
175
|
+
when :always then true
|
|
224
176
|
end
|
|
225
|
-
expand_workspace_globs(lerna['packages'] || ['packages/*'])
|
|
226
177
|
end
|
|
227
178
|
|
|
228
|
-
def
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
path = File.join(@dir, candidate)
|
|
232
|
-
next unless Dir.exist?(path)
|
|
233
|
-
|
|
234
|
-
subdirs = Dir.entries(path).reject { |e| e.start_with?('.') }.select do |e|
|
|
235
|
-
File.directory?(File.join(path, e))
|
|
236
|
-
end
|
|
237
|
-
packages.concat(subdirs.map { |s| "#{candidate}/#{s}" }) if subdirs.size > 1
|
|
238
|
-
end
|
|
239
|
-
packages
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def expand_workspace_globs(globs)
|
|
243
|
-
globs.flat_map do |glob|
|
|
244
|
-
pattern = File.join(@dir, glob)
|
|
245
|
-
Dir.glob(pattern).select { |p| File.directory?(p) }.map do |p|
|
|
246
|
-
p.sub("#{@dir}/", '')
|
|
247
|
-
end
|
|
248
|
-
end
|
|
249
|
-
end
|
|
250
|
-
|
|
251
|
-
# Framework detection helpers
|
|
252
|
-
|
|
253
|
-
def detect_ruby_framework
|
|
254
|
-
return 'rails' if gemfile_has?('Gemfile', 'rails')
|
|
255
|
-
return 'sinatra' if gemfile_has?('Gemfile', 'sinatra')
|
|
256
|
-
return 'hanami' if gemfile_has?('Gemfile', 'hanami')
|
|
257
|
-
|
|
258
|
-
nil
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
def detect_js_framework
|
|
262
|
-
return 'next' if pkg_has?('next')
|
|
263
|
-
return 'remix' if pkg_has?('@remix-run/react')
|
|
264
|
-
return 'nuxt' if pkg_has?('nuxt')
|
|
265
|
-
return 'svelte' if pkg_has?('svelte') || pkg_has?('@sveltejs/kit')
|
|
266
|
-
return 'react' if pkg_has?('react')
|
|
267
|
-
return 'vue' if pkg_has?('vue')
|
|
268
|
-
return 'express' if pkg_has?('express')
|
|
269
|
-
|
|
270
|
-
nil
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
def detect_python_framework
|
|
274
|
-
return 'django' if exists?('manage.py') || pip_has?('django')
|
|
275
|
-
return 'flask' if pip_has?('flask')
|
|
276
|
-
return 'fastapi' if pip_has?('fastapi')
|
|
277
|
-
|
|
278
|
-
nil
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def detect_rust_framework
|
|
282
|
-
content = read_file('Cargo.toml')
|
|
283
|
-
return 'actix' if content.include?('actix-web')
|
|
284
|
-
return 'axum' if content.include?('axum')
|
|
285
|
-
return 'rocket' if content.include?('rocket')
|
|
286
|
-
|
|
287
|
-
nil
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def detect_go_framework
|
|
291
|
-
content = read_file('go.mod')
|
|
292
|
-
return 'gin' if content.include?('gin-gonic')
|
|
293
|
-
return 'echo' if content.include?('labstack/echo')
|
|
294
|
-
return 'fiber' if content.include?('gofiber/fiber')
|
|
295
|
-
return 'chi' if content.include?('go-chi/chi')
|
|
296
|
-
|
|
297
|
-
nil
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
# File helpers
|
|
301
|
-
|
|
302
|
-
def exists?(filename)
|
|
303
|
-
File.exist?(File.join(@dir, filename))
|
|
304
|
-
end
|
|
305
|
-
|
|
306
|
-
def read_file(filename)
|
|
307
|
-
path = File.join(@dir, filename)
|
|
308
|
-
File.exist?(path) ? File.read(path) : ''
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
def gemfile_has?(file, gem_name)
|
|
312
|
-
read_file(file).match?(/['"]#{Regexp.escape(gem_name)}[\w-]*['"]/)
|
|
313
|
-
end
|
|
179
|
+
def exists?(filename) = File.exist?(File.join(@dir, filename))
|
|
180
|
+
def read_file(filename) = File.join(@dir, filename).then { |p| File.exist?(p) ? File.read(p) : '' }
|
|
181
|
+
def gemfile_has?(file, gem_name) = read_file(file).match?(/['"]#{Regexp.escape(gem_name)}[\w-]*['"]/)
|
|
314
182
|
|
|
315
183
|
def pkg_has?(package)
|
|
316
184
|
@pkg_json ||= begin
|
|
317
185
|
raw = read_file('package.json')
|
|
318
186
|
raw.empty? ? {} : JSON.parse(raw)
|
|
319
|
-
rescue JSON::ParserError
|
|
187
|
+
rescue JSON::ParserError => e
|
|
188
|
+
warn("Failed to parse package.json: #{e.message}")
|
|
320
189
|
{}
|
|
321
190
|
end
|
|
322
|
-
|
|
323
191
|
deps = (@pkg_json['dependencies'] || {}).merge(@pkg_json['devDependencies'] || {})
|
|
324
192
|
deps.key?(package)
|
|
325
193
|
end
|
data/lib/ocak/stream_parser.rb
CHANGED
data/lib/ocak.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ocak
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Clay Harmon
|
|
@@ -56,6 +56,7 @@ files:
|
|
|
56
56
|
- lib/ocak/commands/clean.rb
|
|
57
57
|
- lib/ocak/commands/debt.rb
|
|
58
58
|
- lib/ocak/commands/design.rb
|
|
59
|
+
- lib/ocak/commands/hiz.rb
|
|
59
60
|
- lib/ocak/commands/init.rb
|
|
60
61
|
- lib/ocak/commands/resume.rb
|
|
61
62
|
- lib/ocak/commands/run.rb
|
|
@@ -64,6 +65,8 @@ files:
|
|
|
64
65
|
- lib/ocak/issue_fetcher.rb
|
|
65
66
|
- lib/ocak/logger.rb
|
|
66
67
|
- lib/ocak/merge_manager.rb
|
|
68
|
+
- lib/ocak/monorepo_detector.rb
|
|
69
|
+
- lib/ocak/pipeline_executor.rb
|
|
67
70
|
- lib/ocak/pipeline_runner.rb
|
|
68
71
|
- lib/ocak/pipeline_state.rb
|
|
69
72
|
- lib/ocak/planner.rb
|