fun_ci 1.2.1 → 2.0.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/CHANGELOG.md +218 -0
- data/README.md +180 -59
- data/lib/fun_ci/agent/age.rb +15 -0
- data/lib/fun_ci/agent/command_support.rb +27 -0
- data/lib/fun_ci/agent/commands.rb +27 -0
- data/lib/fun_ci/agent/context.rb +10 -0
- data/lib/fun_ci/agent/digest.rb +49 -0
- data/lib/fun_ci/agent/events.rb +47 -0
- data/lib/fun_ci/agent/events_command.rb +61 -0
- data/lib/fun_ci/agent/evidence_text.rb +46 -0
- data/lib/fun_ci/agent/exit_code.rb +11 -0
- data/lib/fun_ci/agent/git.rb +31 -0
- data/lib/fun_ci/agent/live_pipeline.rb +35 -0
- data/lib/fun_ci/agent/options.rb +75 -0
- data/lib/fun_ci/agent/output.rb +81 -0
- data/lib/fun_ci/agent/report_stage.rb +73 -0
- data/lib/fun_ci/agent/reports.rb +47 -0
- data/lib/fun_ci/agent/run_report.rb +30 -0
- data/lib/fun_ci/agent/runs_command.rb +34 -0
- data/lib/fun_ci/agent/runs_text.rb +27 -0
- data/lib/fun_ci/agent/snapshots.rb +39 -0
- data/lib/fun_ci/agent/stage_summary.rb +30 -0
- data/lib/fun_ci/agent/status_command.rb +36 -0
- data/lib/fun_ci/agent/status_json.rb +27 -0
- data/lib/fun_ci/agent/status_text.rb +37 -0
- data/lib/fun_ci/agent/system_clock.rb +11 -0
- data/lib/fun_ci/agent/verdict.rb +32 -0
- data/lib/fun_ci/agent/wait_command.rb +71 -0
- data/lib/fun_ci/agent/waiting.rb +32 -0
- data/lib/fun_ci/agent/why_command.rb +50 -0
- data/lib/fun_ci/agent/why_json.rb +35 -0
- data/lib/fun_ci/agent/why_text.rb +43 -0
- data/lib/fun_ci/cli.rb +72 -94
- data/lib/fun_ci/cli_help.rb +65 -0
- data/lib/fun_ci/console/board_data.rb +66 -0
- data/lib/fun_ci/console/board_heartbeat.rb +40 -0
- data/lib/fun_ci/console/console_events.rb +20 -0
- data/lib/fun_ci/console/console_log.rb +26 -0
- data/lib/fun_ci/console/console_loop.rb +41 -0
- data/lib/fun_ci/console/console_session.rb +82 -0
- data/lib/fun_ci/console/console_state.rb +42 -0
- data/lib/fun_ci/console/key_handler.rb +73 -0
- data/lib/fun_ci/console/launcher.rb +43 -0
- data/lib/fun_ci/console/milestone_events.rb +38 -0
- data/lib/fun_ci/console/milestones.rb +25 -0
- data/lib/fun_ci/console/renderer_lookup.rb +51 -0
- data/lib/fun_ci/console/renderer_process.rb +60 -0
- data/lib/fun_ci/console/run_message.rb +33 -0
- data/lib/fun_ci/console/stage_change_detector.rb +32 -0
- data/lib/fun_ci/console/stage_events.rb +31 -0
- data/lib/fun_ci/console/stop_report.rb +39 -0
- data/lib/fun_ci/console/streak_counter.rb +19 -0
- data/lib/fun_ci/console/view.rb +47 -0
- data/lib/fun_ci/evidence/about.rb +12 -0
- data/lib/fun_ci/evidence/about_stage.rb +29 -0
- data/lib/fun_ci/evidence/caps.rb +77 -0
- data/lib/fun_ci/evidence/catalog.rb +83 -0
- data/lib/fun_ci/evidence/collector.rb +95 -0
- data/lib/fun_ci/evidence/command_output.rb +45 -0
- data/lib/fun_ci/evidence/command_reading.rb +38 -0
- data/lib/fun_ci/evidence/command_runner.rb +75 -0
- data/lib/fun_ci/evidence/context.rb +21 -0
- data/lib/fun_ci/evidence/context_document.rb +29 -0
- data/lib/fun_ci/evidence/deadline.rb +15 -0
- data/lib/fun_ci/evidence/detection.rb +82 -0
- data/lib/fun_ci/evidence/document.rb +59 -0
- data/lib/fun_ci/evidence/extract_command.rb +83 -0
- data/lib/fun_ci/evidence/extract_options.rb +45 -0
- data/lib/fun_ci/evidence/extraction.rb +54 -0
- data/lib/fun_ci/evidence/extractors/command.rb +52 -0
- data/lib/fun_ci/evidence/extractors/grep.rb +49 -0
- data/lib/fun_ci/evidence/extractors/json_log.rb +45 -0
- data/lib/fun_ci/evidence/extractors/junit_files.rb +32 -0
- data/lib/fun_ci/evidence/extractors/last_lines.rb +25 -0
- data/lib/fun_ci/evidence/extractors/log_file.rb +62 -0
- data/lib/fun_ci/evidence/extractors/process_tree.rb +51 -0
- data/lib/fun_ci/evidence/extractors/section.rb +49 -0
- data/lib/fun_ci/evidence/findings.rb +14 -0
- data/lib/fun_ci/evidence/line_ranges.rb +21 -0
- data/lib/fun_ci/evidence/line_scan.rb +22 -0
- data/lib/fun_ci/evidence/log_record.rb +58 -0
- data/lib/fun_ci/evidence/masking.rb +55 -0
- data/lib/fun_ci/evidence/option_check.rb +53 -0
- data/lib/fun_ci/evidence/outcome.rb +15 -0
- data/lib/fun_ci/evidence/patterns.rb +22 -0
- data/lib/fun_ci/evidence/presets/README.md +7 -0
- data/lib/fun_ci/evidence/presets/bun-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/cargo-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/dart-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/deno-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/dotnet-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/ecs.yml +3 -0
- data/lib/fun_ci/evidence/presets/eslint.yml +6 -0
- data/lib/fun_ci/evidence/presets/exunit.yml +6 -0
- data/lib/fun_ci/evidence/presets/gcc.yml +6 -0
- data/lib/fun_ci/evidence/presets/go-build.yml +5 -0
- data/lib/fun_ci/evidence/presets/go-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/gradle.yml +6 -0
- data/lib/fun_ci/evidence/presets/gtest.yml +6 -0
- data/lib/fun_ci/evidence/presets/jest.yml +6 -0
- data/lib/fun_ci/evidence/presets/logstash.yml +3 -0
- data/lib/fun_ci/evidence/presets/maven.yml +6 -0
- data/lib/fun_ci/evidence/presets/minitest.yml +6 -0
- data/lib/fun_ci/evidence/presets/mocha.yml +6 -0
- data/lib/fun_ci/evidence/presets/mypy.yml +6 -0
- data/lib/fun_ci/evidence/presets/node-test.yml +6 -0
- data/lib/fun_ci/evidence/presets/phpunit.yml +6 -0
- data/lib/fun_ci/evidence/presets/pino.yml +5 -0
- data/lib/fun_ci/evidence/presets/prove.yml +6 -0
- data/lib/fun_ci/evidence/presets/pytest.yml +6 -0
- data/lib/fun_ci/evidence/presets/rspec.yml +6 -0
- data/lib/fun_ci/evidence/presets/rubocop.yml +6 -0
- data/lib/fun_ci/evidence/presets/ruff.yml +6 -0
- data/lib/fun_ci/evidence/presets/rustc.yml +6 -0
- data/lib/fun_ci/evidence/presets/shellcheck.yml +5 -0
- data/lib/fun_ci/evidence/presets/structlog.yml +4 -0
- data/lib/fun_ci/evidence/presets/swift-test.yml +5 -0
- data/lib/fun_ci/evidence/presets/tsc.yml +6 -0
- data/lib/fun_ci/evidence/presets/unittest.yml +6 -0
- data/lib/fun_ci/evidence/presets/vitest.yml +6 -0
- data/lib/fun_ci/evidence/presets.rb +30 -0
- data/lib/fun_ci/evidence/problem.rb +9 -0
- data/lib/fun_ci/evidence/process_table.rb +40 -0
- data/lib/fun_ci/evidence/saved_run.rb +22 -0
- data/lib/fun_ci/evidence/section_walk.rb +49 -0
- data/lib/fun_ci/evidence/settings.rb +64 -0
- data/lib/fun_ci/evidence/settings_check.rb +78 -0
- data/lib/fun_ci/evidence/source.rb +34 -0
- data/lib/fun_ci/evidence/sources.rb +21 -0
- data/lib/fun_ci/evidence/stage_context.rb +30 -0
- data/lib/fun_ci/evidence/stamp.rb +10 -0
- data/lib/fun_ci/evidence/start.rb +36 -0
- data/lib/fun_ci/evidence/worktree.rb +62 -0
- data/lib/fun_ci/persistence/active_runs.rb +69 -0
- data/lib/fun_ci/persistence/database.rb +59 -30
- data/lib/fun_ci/persistence/null_recorder.rb +25 -0
- data/lib/fun_ci/persistence/output_tail.rb +24 -0
- data/lib/fun_ci/persistence/pipeline_recorder.rb +78 -32
- data/lib/fun_ci/persistence/pipeline_run.rb +26 -17
- data/lib/fun_ci/persistence/project_runs.rb +52 -0
- data/lib/fun_ci/persistence/raw_outputs.rb +46 -0
- data/lib/fun_ci/persistence/retention.rb +25 -0
- data/lib/fun_ci/persistence/run_status.rb +31 -0
- data/lib/fun_ci/persistence/stage_job.rb +58 -13
- data/lib/fun_ci/persistence/state_dir.rb +26 -0
- data/lib/fun_ci/persistence/write_trouble.rb +46 -0
- data/lib/fun_ci/pipeline/background_fork.rb +42 -0
- data/lib/fun_ci/pipeline/command_executor.rb +58 -0
- data/lib/fun_ci/pipeline/gate.rb +35 -0
- data/lib/fun_ci/pipeline/git_environment.rb +16 -0
- data/lib/fun_ci/pipeline/head_part.rb +40 -0
- data/lib/fun_ci/pipeline/in_place.rb +17 -0
- data/lib/fun_ci/pipeline/output_window.rb +73 -0
- data/lib/fun_ci/pipeline/pipeline_forker.rb +18 -16
- data/lib/fun_ci/pipeline/process_runner.rb +90 -17
- data/lib/fun_ci/pipeline/run_canceller.rb +75 -0
- data/lib/fun_ci/pipeline/slot.rb +30 -0
- data/lib/fun_ci/pipeline/slot_lock.rb +13 -0
- data/lib/fun_ci/pipeline/slot_run.rb +86 -0
- data/lib/fun_ci/pipeline/stage_dir.rb +50 -0
- data/lib/fun_ci/pipeline/stage_end.rb +70 -0
- data/lib/fun_ci/pipeline/stage_execution.rb +67 -0
- data/lib/fun_ci/pipeline/stage_runner.rb +42 -65
- data/lib/fun_ci/pipeline/stale_pipeline_canceller.rb +16 -33
- data/lib/fun_ci/pipeline/tail_segments.rb +60 -0
- data/lib/fun_ci/pipeline/test_report.rb +48 -0
- data/lib/fun_ci/pipeline/trigger.rb +55 -115
- data/lib/fun_ci/pipeline/trigger_command.rb +62 -0
- data/lib/fun_ci/pipeline/trigger_params.rb +48 -0
- data/lib/fun_ci/pipeline/workspaces.rb +22 -0
- data/lib/fun_ci/pipeline/worktree_pool.rb +66 -0
- data/lib/fun_ci/pipeline/worktree_prune.rb +49 -0
- data/lib/fun_ci/pipeline/worktrees.rb +45 -0
- data/lib/fun_ci/setup/agent_instructions.rb +31 -0
- data/lib/fun_ci/setup/commands.rb +49 -0
- data/lib/fun_ci/setup/git_hooks.rb +20 -0
- data/lib/fun_ci/setup/hook_script.rb +47 -0
- data/lib/fun_ci/setup/hook_writer.rb +13 -31
- data/lib/fun_ci/setup/installer.rb +39 -23
- data/lib/fun_ci/setup/legacy_hooks.rb +38 -0
- data/lib/fun_ci/setup/project_config.rb +43 -17
- data/lib/fun_ci/setup/project_detector.rb +39 -7
- data/lib/fun_ci/setup/settings.rb +36 -0
- data/lib/fun_ci/setup/setup_checker.rb +18 -12
- data/lib/fun_ci/setup/stage_templates/javascript.rb +18 -0
- data/lib/fun_ci/setup/stage_templates/managed.rb +24 -0
- data/lib/fun_ci/setup/stage_templates/native.rb +25 -0
- data/lib/fun_ci/setup/stage_templates/scripting.rb +27 -0
- data/lib/fun_ci/setup/stage_templates/shell.rb +16 -0
- data/lib/fun_ci/setup/stage_templates.rb +25 -0
- data/lib/fun_ci/setup/template_writer.rb +11 -36
- data/lib/fun_ci/version.rb +1 -1
- data/lib/fun_ci.rb +0 -1
- metadata +205 -41
- data/exe/fun-ci-trigger +0 -19
- data/exe/fun-ci-tui +0 -23
- data/lib/fun_ci/animations/celebrate.rb +0 -210
- data/lib/fun_ci/animations/explosion.rb +0 -138
- data/lib/fun_ci/animations/flash.rb +0 -205
- data/lib/fun_ci/animations/idle.rb +0 -127
- data/lib/fun_ci/animations/leprechauns.rb +0 -240
- data/lib/fun_ci/animations/running.rb +0 -129
- data/lib/fun_ci/animations/success.rb +0 -281
- data/lib/fun_ci/animations/yay.rb +0 -169
- data/lib/fun_ci/persistence/state_machine.rb +0 -50
- data/lib/fun_ci/pipeline/background_wrapper.rb +0 -27
- data/lib/fun_ci/tui/admin_tui.rb +0 -90
- data/lib/fun_ci/tui/animation.rb +0 -49
- data/lib/fun_ci/tui/animation_compositor.rb +0 -107
- data/lib/fun_ci/tui/animation_frames.rb +0 -112
- data/lib/fun_ci/tui/animation_library.rb +0 -46
- data/lib/fun_ci/tui/animation_renderer.rb +0 -144
- data/lib/fun_ci/tui/ansi.rb +0 -34
- data/lib/fun_ci/tui/board_data.rb +0 -53
- data/lib/fun_ci/tui/board_renderer.rb +0 -105
- data/lib/fun_ci/tui/duration_formatter.rb +0 -24
- data/lib/fun_ci/tui/header_animation_manager.rb +0 -71
- data/lib/fun_ci/tui/header_animation_player.rb +0 -45
- data/lib/fun_ci/tui/key_handler.rb +0 -86
- data/lib/fun_ci/tui/looping_animation_player.rb +0 -45
- data/lib/fun_ci/tui/relative_time.rb +0 -22
- data/lib/fun_ci/tui/row_formatter.rb +0 -108
- data/lib/fun_ci/tui/screen.rb +0 -103
- data/lib/fun_ci/tui/spinner.rb +0 -24
- data/lib/fun_ci/tui/stage_change_detector.rb +0 -58
- data/lib/fun_ci/tui/streak_counter.rb +0 -29
- data/lib/fun_ci/tui/terminal_input.rb +0 -69
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "shellwords"
|
|
4
|
+
require_relative "settings"
|
|
5
|
+
require_relative "../evidence/settings"
|
|
6
|
+
require_relative "../evidence/start"
|
|
7
|
+
|
|
3
8
|
module FunCi
|
|
4
9
|
module Setup
|
|
5
10
|
class ProjectConfig
|
|
@@ -15,28 +20,49 @@ module FunCi
|
|
|
15
20
|
end
|
|
16
21
|
|
|
17
22
|
def validate
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
errors << "No .fun-ci/ folder found in #{@project_root}"
|
|
22
|
-
return errors
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
REQUIRED_SCRIPTS.each do |script|
|
|
26
|
-
path = File.join(@fun_ci_dir, script)
|
|
27
|
-
if !File.exist?(path)
|
|
28
|
-
errors << ".fun-ci/#{script} is not found"
|
|
29
|
-
elsif !File.executable?(path)
|
|
30
|
-
errors << ".fun-ci/#{script} is not executable"
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
errors
|
|
23
|
+
return ["No .fun-ci/ folder found in #{@project_root}"] unless folder_exists?
|
|
24
|
+
|
|
25
|
+
REQUIRED_SCRIPTS.flat_map { |script| script_errors(script) } + settings.errors
|
|
35
26
|
end
|
|
36
27
|
|
|
28
|
+
# Mistakes in the `evidence` key, which `fun-ci check` reports but which
|
|
29
|
+
# never stop a pipeline: the entry with the mistake is left out instead.
|
|
30
|
+
def evidence_errors = Evidence::Settings.load(File.join(@fun_ci_dir, "config")).errors(root: @project_root)
|
|
31
|
+
|
|
32
|
+
# The presets whose markers the project has, which run when a stage fails
|
|
33
|
+
# and its output shows their tool.
|
|
34
|
+
def presets = preset_candidates.select(&:because).map { |found| found.preset.name }
|
|
35
|
+
|
|
36
|
+
# The presets without markers, which read what any project's output
|
|
37
|
+
# may hold, such as JSON logs.
|
|
38
|
+
def any_project_presets = preset_candidates.reject(&:because).map { |found| found.preset.name }
|
|
39
|
+
|
|
40
|
+
def worktree_slots = settings.worktree_slots
|
|
41
|
+
|
|
37
42
|
def script_path(stage)
|
|
38
43
|
File.join(@fun_ci_dir, "#{stage}.sh")
|
|
39
44
|
end
|
|
45
|
+
|
|
46
|
+
# The shell command that runs a stage's script on a commit, its path
|
|
47
|
+
# quoted, since a project's path may hold spaces.
|
|
48
|
+
def stage_command(stage, sha) = "#{Shellwords.escape(script_path(stage))} #{sha}"
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def settings = Settings.new(File.join(@fun_ci_dir, "config"))
|
|
53
|
+
|
|
54
|
+
def preset_candidates
|
|
55
|
+
evidence = Evidence::Settings.load(File.join(@fun_ci_dir, "config"))
|
|
56
|
+
Evidence::Start.candidates(Evidence::Worktree.new(@project_root), evidence, "all")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def script_errors(script)
|
|
60
|
+
path = File.join(@fun_ci_dir, script)
|
|
61
|
+
return [".fun-ci/#{script} is not found"] unless File.exist?(path)
|
|
62
|
+
return [".fun-ci/#{script} is not executable"] unless File.executable?(path)
|
|
63
|
+
|
|
64
|
+
[]
|
|
65
|
+
end
|
|
40
66
|
end
|
|
41
67
|
end
|
|
42
68
|
end
|
|
@@ -2,21 +2,53 @@
|
|
|
2
2
|
|
|
3
3
|
module FunCi
|
|
4
4
|
module Setup
|
|
5
|
+
# Which kind of project a directory is, from the names at its top: the
|
|
6
|
+
# first stack, in this order, any of whose markers is there. A marker is a
|
|
7
|
+
# name, a glob, or a list of them that must all be there. A language
|
|
8
|
+
# comes before the tooling that sits beside it (Node beside Python, a
|
|
9
|
+
# Makefile around Go), and a lockfile picks the package manager.
|
|
5
10
|
class ProjectDetector
|
|
11
|
+
STACKS = {
|
|
12
|
+
ruby_rspec: [%w[Gemfile spec], %w[Gemfile .rspec]],
|
|
13
|
+
ruby_bundler: ["Gemfile"],
|
|
14
|
+
jvm_gradle_kotlin: %w[build.gradle.kts settings.gradle.kts],
|
|
15
|
+
jvm_gradle_groovy: %w[build.gradle settings.gradle],
|
|
16
|
+
jvm_maven: ["pom.xml"],
|
|
17
|
+
rust_cargo: ["Cargo.toml"],
|
|
18
|
+
go: ["go.mod"],
|
|
19
|
+
elixir_mix: ["mix.exs"],
|
|
20
|
+
dart: ["pubspec.yaml"],
|
|
21
|
+
swift: ["Package.swift"],
|
|
22
|
+
php_composer: ["composer.json"],
|
|
23
|
+
dotnet: %w[*.sln *.csproj *.fsproj],
|
|
24
|
+
python_uv: ["uv.lock"],
|
|
25
|
+
python_poetry: ["poetry.lock"],
|
|
26
|
+
python: %w[pyproject.toml setup.py setup.cfg requirements.txt],
|
|
27
|
+
deno: %w[deno.json deno.jsonc],
|
|
28
|
+
bun: %w[bun.lock bun.lockb bunfig.toml],
|
|
29
|
+
node_pnpm: ["pnpm-lock.yaml"],
|
|
30
|
+
node_yarn: ["yarn.lock"],
|
|
31
|
+
node_npm: ["package.json"],
|
|
32
|
+
perl: %w[cpanfile Makefile.PL Build.PL dist.ini],
|
|
33
|
+
cmake: ["CMakeLists.txt"],
|
|
34
|
+
make: %w[Makefile makefile GNUmakefile]
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
6
37
|
def initialize(filenames)
|
|
7
38
|
@filenames = filenames
|
|
8
39
|
end
|
|
9
40
|
|
|
10
41
|
def detect
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return :jvm_gradle_kotlin if @filenames.include?("settings.gradle.kts")
|
|
14
|
-
return :jvm_gradle_groovy if @filenames.include?("build.gradle")
|
|
15
|
-
return :jvm_gradle_groovy if @filenames.include?("settings.gradle")
|
|
16
|
-
return :jvm_maven if @filenames.include?("pom.xml")
|
|
42
|
+
STACKS.find { |_, markers| markers.any? { |marker| present?(marker) } }&.first || :unknown
|
|
43
|
+
end
|
|
17
44
|
|
|
18
|
-
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def present?(marker)
|
|
48
|
+
Array(marker).all? { |part| @filenames.any? { |name| names?(part, name) } }
|
|
19
49
|
end
|
|
50
|
+
|
|
51
|
+
def names?(part, name) = File.fnmatch?(part, name)
|
|
20
52
|
end
|
|
21
53
|
end
|
|
22
54
|
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
module FunCi
|
|
6
|
+
module Setup
|
|
7
|
+
# .fun-ci/config: settings for this machine's runs, as YAML. Each one left
|
|
8
|
+
# out takes its default; one that is wrong is reported and the default used,
|
|
9
|
+
# as are all of them when the file isn't YAML.
|
|
10
|
+
class Settings
|
|
11
|
+
DEFAULTS = { "worktree_slots" => 2 }.freeze
|
|
12
|
+
|
|
13
|
+
def initialize(path)
|
|
14
|
+
@path = path
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def worktree_slots = errors.empty? ? values.fetch("worktree_slots") : DEFAULTS.fetch("worktree_slots")
|
|
18
|
+
|
|
19
|
+
def errors
|
|
20
|
+
return [".fun-ci/config must be a mapping such as `worktree_slots: 2`"] unless raw.is_a?(Hash)
|
|
21
|
+
|
|
22
|
+
slots = values["worktree_slots"]
|
|
23
|
+
return [] if slots.is_a?(Integer) && slots.positive?
|
|
24
|
+
|
|
25
|
+
[".fun-ci/config: worktree_slots must be a whole number above 0, not #{slots.inspect}"]
|
|
26
|
+
rescue Psych::SyntaxError => e
|
|
27
|
+
[".fun-ci/config is not YAML: #{e.problem} (line #{e.line})"]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def raw = File.exist?(@path) ? YAML.safe_load_file(@path) || {} : {}
|
|
33
|
+
def values = DEFAULTS.merge(raw)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "project_config"
|
|
4
|
+
require_relative "legacy_hooks"
|
|
4
5
|
|
|
5
6
|
module FunCi
|
|
6
7
|
module Setup
|
|
7
8
|
class SetupChecker
|
|
8
|
-
def self.run(project_root:, stdout: $stdout
|
|
9
|
-
new(project_root: project_root, stdout: stdout).run
|
|
9
|
+
def self.run(project_root:, stdout: $stdout)
|
|
10
|
+
new(config: ProjectConfig.new(project_root), hooks: LegacyHooks.new(project_root), stdout: stdout).run
|
|
10
11
|
end
|
|
11
12
|
|
|
12
|
-
def initialize(
|
|
13
|
-
@config =
|
|
13
|
+
def initialize(config:, hooks:, stdout:)
|
|
14
|
+
@config = config
|
|
15
|
+
@hooks = hooks
|
|
14
16
|
@stdout = stdout
|
|
15
17
|
end
|
|
16
18
|
|
|
19
|
+
# Warnings are printed but don't fail the check.
|
|
17
20
|
def run
|
|
18
|
-
errors = @config.validate
|
|
21
|
+
errors = @config.validate + @config.evidence_errors
|
|
22
|
+
@stdout.puts(errors.empty? ? "All OK. The project is configured." : errors)
|
|
23
|
+
list_presets
|
|
24
|
+
@hooks.warnings.each { |warning| @stdout.puts "Warning: #{warning}" }
|
|
25
|
+
errors.empty? ? 0 : 1
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
19
29
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
else
|
|
24
|
-
errors.each { |e| @stdout.puts e }
|
|
25
|
-
1
|
|
26
|
-
end
|
|
30
|
+
def list_presets
|
|
31
|
+
lists = { "this project" => @config.presets, "any project's output" => @config.any_project_presets }
|
|
32
|
+
lists.each { |what, names| @stdout.puts "Evidence presets for #{what}: #{names.join(", ")}" if names.any? }
|
|
27
33
|
end
|
|
28
34
|
end
|
|
29
35
|
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "shell"
|
|
4
|
+
|
|
5
|
+
module FunCi
|
|
6
|
+
module Setup
|
|
7
|
+
# JavaScript and TypeScript: Deno, Bun, and Node with its package managers.
|
|
8
|
+
module StageTemplates
|
|
9
|
+
JAVASCRIPT = {
|
|
10
|
+
deno: stages("deno lint", "deno check", "deno test", "deno task test:slow"),
|
|
11
|
+
bun: stages("bunx eslint .", "bun install", "bun test", "bun run test:slow"),
|
|
12
|
+
node_pnpm: stages("pnpm exec eslint .", "pnpm install", "pnpm test", "pnpm run test:slow"),
|
|
13
|
+
node_yarn: stages("yarn eslint .", "yarn install", "yarn test", "yarn run test:slow"),
|
|
14
|
+
node_npm: stages("npx eslint .", "npm install", "npm test", "npm run test:slow")
|
|
15
|
+
}.freeze
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "shell"
|
|
4
|
+
|
|
5
|
+
module FunCi
|
|
6
|
+
module Setup
|
|
7
|
+
# Stacks that run on a virtual machine: the JVM, .NET, the BEAM and Dart's.
|
|
8
|
+
module StageTemplates
|
|
9
|
+
GRADLE = stages("./gradlew check -x test", "./gradlew assemble", "./gradlew test", "./gradlew integrationTest")
|
|
10
|
+
.freeze
|
|
11
|
+
|
|
12
|
+
MANAGED = {
|
|
13
|
+
jvm_gradle_kotlin: GRADLE,
|
|
14
|
+
jvm_gradle_groovy: GRADLE,
|
|
15
|
+
jvm_maven: stages("mvn verify -DskipTests", "mvn compile", "mvn test", "mvn verify"),
|
|
16
|
+
dotnet: stages("dotnet format --verify-no-changes", "dotnet build",
|
|
17
|
+
'dotnet test --filter "Category!=Slow"', 'dotnet test --filter "Category=Slow"'),
|
|
18
|
+
elixir_mix: stages("mix format --check-formatted", "mix deps.get && mix compile",
|
|
19
|
+
"mix test --exclude slow", "mix test --only slow"),
|
|
20
|
+
dart: stages("dart analyze", "dart pub get", "dart test --exclude-tags slow", "dart test --tags slow")
|
|
21
|
+
}.freeze
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "shell"
|
|
4
|
+
|
|
5
|
+
module FunCi
|
|
6
|
+
module Setup
|
|
7
|
+
# Stacks that compile to the machine: Rust, Go, Swift, and C or C++ built
|
|
8
|
+
# with CMake or make.
|
|
9
|
+
module StageTemplates
|
|
10
|
+
CTEST = "ctest --test-dir build --output-on-failure --no-tests=error"
|
|
11
|
+
CLANG_FORMAT = "git ls-files -z '*.c' '*.cc' '*.cpp' '*.h' '*.hpp' | xargs -0 clang-format --dry-run --Werror"
|
|
12
|
+
|
|
13
|
+
NATIVE = {
|
|
14
|
+
rust_cargo: stages("cargo clippy --all-targets -- -D warnings", "cargo build --all-targets",
|
|
15
|
+
"cargo test", "cargo test -- --ignored"),
|
|
16
|
+
go: stages("go vet ./...", "go build ./...", "go test -short ./...", "go test ./..."),
|
|
17
|
+
swift: stages("swift format lint --recursive --strict .", "swift build --build-tests",
|
|
18
|
+
"swift test --skip Slow", "swift test --filter Slow"),
|
|
19
|
+
cmake: stages(CLANG_FORMAT, "cmake -S . -B build && cmake --build build",
|
|
20
|
+
"#{CTEST} -LE slow", "#{CTEST} -L slow"),
|
|
21
|
+
make: stages("make lint", "make", "make test", "make test-slow")
|
|
22
|
+
}.freeze
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "shell"
|
|
4
|
+
|
|
5
|
+
module FunCi
|
|
6
|
+
module Setup
|
|
7
|
+
# Interpreted stacks: Ruby, Python, PHP and Perl.
|
|
8
|
+
module StageTemplates
|
|
9
|
+
def self.python(runner, build)
|
|
10
|
+
stages("#{runner}ruff check .", build, "#{runner}pytest -m \"not slow\"", "#{runner}pytest -m slow")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
SCRIPTING = {
|
|
14
|
+
ruby_rspec: stages("bundle exec rubocop", "bundle install --quiet",
|
|
15
|
+
"bundle exec rspec --tag ~slow", "bundle exec rspec --tag slow"),
|
|
16
|
+
ruby_bundler: stages("bundle exec rubocop", "bundle install --quiet",
|
|
17
|
+
"bundle exec rake test", "bundle exec rake test:slow"),
|
|
18
|
+
php_composer: stages("vendor/bin/phpcs", "composer install --no-interaction --no-progress",
|
|
19
|
+
"vendor/bin/phpunit --exclude-group slow", "vendor/bin/phpunit --group slow"),
|
|
20
|
+
python_uv: python("uv run ", "uv sync"),
|
|
21
|
+
python_poetry: python("poetry run ", "poetry install"),
|
|
22
|
+
python: python("python3 -m ", "python3 -m compileall -q ."),
|
|
23
|
+
perl: stages("perlcritic lib", "cpanm --installdeps --notest .", "prove -lr t", "prove -lr xt")
|
|
24
|
+
}.freeze
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module FunCi
|
|
4
|
+
module Setup
|
|
5
|
+
# What every stack's stage scripts are made of.
|
|
6
|
+
module StageTemplates
|
|
7
|
+
def self.script(body) = "#!/bin/sh\n#{body}\n"
|
|
8
|
+
|
|
9
|
+
def self.stages(lint, build, fast, slow)
|
|
10
|
+
{ "lint.sh" => lint, "build.sh" => build, "fast.sh" => fast, "slow.sh" => slow }.transform_values do |body|
|
|
11
|
+
script(body)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "stage_templates/managed"
|
|
4
|
+
require_relative "stage_templates/native"
|
|
5
|
+
require_relative "stage_templates/scripting"
|
|
6
|
+
require_relative "stage_templates/javascript"
|
|
7
|
+
|
|
8
|
+
module FunCi
|
|
9
|
+
module Setup
|
|
10
|
+
# The stage scripts `fun-ci init` writes, per kind of project (AT-10.21).
|
|
11
|
+
# Test stages run the fast suite without the tests the tool's convention
|
|
12
|
+
# marks slow, and the slow suite with only those; where the tool has no
|
|
13
|
+
# such convention, the slow suite is a `test:slow` script (`test-slow`,
|
|
14
|
+
# for make).
|
|
15
|
+
module StageTemplates
|
|
16
|
+
TEMPLATES = [MANAGED, NATIVE, SCRIPTING, JAVASCRIPT].reduce(:merge).freeze
|
|
17
|
+
|
|
18
|
+
# { "lint.sh" => script, ... }; +lint_override+ replaces the lint command.
|
|
19
|
+
def self.scripts(template_id, lint_override: nil)
|
|
20
|
+
scripts = TEMPLATES.fetch(template_id)
|
|
21
|
+
lint_override ? scripts.merge("lint.sh" => script(lint_override)) : scripts
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -1,52 +1,27 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "stage_templates"
|
|
4
|
+
|
|
3
5
|
module FunCi
|
|
4
6
|
module Setup
|
|
7
|
+
# Writes a project's .fun-ci/ stage scripts, executable.
|
|
5
8
|
class TemplateWriter
|
|
6
|
-
TEMPLATES = {
|
|
7
|
-
ruby_bundler: {
|
|
8
|
-
"lint.sh" => "#!/bin/sh\nbundle exec rubocop\n",
|
|
9
|
-
"build.sh" => "#!/bin/sh\nbundle install --quiet\n",
|
|
10
|
-
"fast.sh" => "#!/bin/sh\nbundle exec rake test\n",
|
|
11
|
-
"slow.sh" => "#!/bin/sh\nbundle exec rake test:slow\n"
|
|
12
|
-
},
|
|
13
|
-
jvm_gradle_kotlin: {
|
|
14
|
-
"lint.sh" => "#!/bin/sh\n./gradlew check -x test\n",
|
|
15
|
-
"build.sh" => "#!/bin/sh\n./gradlew assemble\n",
|
|
16
|
-
"fast.sh" => "#!/bin/sh\n./gradlew test\n",
|
|
17
|
-
"slow.sh" => "#!/bin/sh\n./gradlew integrationTest\n"
|
|
18
|
-
},
|
|
19
|
-
jvm_gradle_groovy: {
|
|
20
|
-
"lint.sh" => "#!/bin/sh\n./gradlew check -x test\n",
|
|
21
|
-
"build.sh" => "#!/bin/sh\n./gradlew assemble\n",
|
|
22
|
-
"fast.sh" => "#!/bin/sh\n./gradlew test\n",
|
|
23
|
-
"slow.sh" => "#!/bin/sh\n./gradlew integrationTest\n"
|
|
24
|
-
},
|
|
25
|
-
jvm_maven: {
|
|
26
|
-
"lint.sh" => "#!/bin/sh\nmvn verify -DskipTests\n",
|
|
27
|
-
"build.sh" => "#!/bin/sh\nmvn compile\n",
|
|
28
|
-
"fast.sh" => "#!/bin/sh\nmvn test\n",
|
|
29
|
-
"slow.sh" => "#!/bin/sh\nmvn verify\n"
|
|
30
|
-
}
|
|
31
|
-
}.freeze
|
|
32
|
-
|
|
33
9
|
def initialize(template_id, target_dir, lint_override: nil)
|
|
34
|
-
@
|
|
10
|
+
@scripts = StageTemplates.scripts(template_id, lint_override: lint_override)
|
|
35
11
|
@target_dir = target_dir
|
|
36
|
-
@lint_override = lint_override
|
|
37
12
|
end
|
|
38
13
|
|
|
39
14
|
def write
|
|
40
|
-
scripts = TEMPLATES.fetch(@template_id)
|
|
41
15
|
fun_ci_dir = File.join(@target_dir, ".fun-ci")
|
|
42
16
|
Dir.mkdir(fun_ci_dir)
|
|
17
|
+
@scripts.each { |name, content| write_script(File.join(fun_ci_dir, name), content) }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
43
21
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
File.write(path, content)
|
|
48
|
-
File.chmod(0o755, path)
|
|
49
|
-
end
|
|
22
|
+
def write_script(path, content)
|
|
23
|
+
File.write(path, content)
|
|
24
|
+
File.chmod(0o755, path)
|
|
50
25
|
end
|
|
51
26
|
end
|
|
52
27
|
end
|
data/lib/fun_ci/version.rb
CHANGED
data/lib/fun_ci.rb
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative "fun_ci/version"
|
|
4
4
|
require_relative "fun_ci/persistence/database"
|
|
5
|
-
require_relative "fun_ci/persistence/state_machine"
|
|
6
5
|
require_relative "fun_ci/persistence/pipeline_run"
|
|
7
6
|
require_relative "fun_ci/persistence/stage_job"
|
|
8
7
|
require_relative "fun_ci/setup/project_config"
|