@codyswann/lisa 4.69.4 → 4.69.5
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.
- package/cdk/create-only/stryker.conf.json +2 -0
- package/cdk/package-lisa/package.lisa.json +10 -6
- package/dist/core/lisa.d.ts +17 -3
- package/dist/core/lisa.d.ts.map +1 -1
- package/dist/core/lisa.js +74 -21
- package/dist/core/lisa.js.map +1 -1
- package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
- package/dist/core/plugin-command.d.ts +21 -0
- package/dist/core/plugin-command.d.ts.map +1 -0
- package/dist/core/plugin-command.js +121 -0
- package/dist/core/plugin-command.js.map +1 -0
- package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
- package/dist/core/upstream-evidence-manifest.js +26 -14
- package/dist/core/upstream-evidence-manifest.js.map +1 -1
- package/eslint-plugin-component-structure/package.json +3 -0
- package/expo/create-only/stryker.conf.json +2 -0
- package/expo/package-lisa/package.lisa.json +3 -3
- package/harper-fabric/package-lisa/package.lisa.json +7 -7
- package/nestjs/package-lisa/package.lisa.json +2 -2
- package/package.json +28 -19
- package/phaser/package-lisa/package.lisa.json +7 -7
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/rails/copy-overwrite/.rubocop.yml +0 -2
- package/rails/copy-overwrite/Gemfile.lisa +8 -8
- package/rails/create-only/.simplecov +16 -14
- package/rails/create-only/spec/spec_helper.rb +2 -0
- package/scripts/verify-npm-advisories-4333.mjs +166 -0
- package/scripts/verify-rails-tooling.rb +175 -0
- package/typescript/create-only/stryker.conf.json +2 -0
- package/typescript/package-lisa/package.lisa.json +10 -6
|
@@ -13,16 +13,16 @@ group :development, :test do
|
|
|
13
13
|
gem "rubocop-rails", "~> 2.30", require: false
|
|
14
14
|
gem "rubocop-performance", "~> 1.24", require: false
|
|
15
15
|
gem "rubocop-rspec", "~> 3.5", require: false
|
|
16
|
-
gem "rubocop-capybara", "~>
|
|
16
|
+
gem "rubocop-capybara", "~> 3.0", require: false
|
|
17
17
|
gem "rubocop-factory_bot", "~> 2.26", require: false
|
|
18
|
-
gem "rubocop-yard", "~>
|
|
18
|
+
gem "rubocop-yard", "~> 1.3", require: false
|
|
19
19
|
|
|
20
20
|
# Testing
|
|
21
|
-
gem "rspec-rails", "~>
|
|
21
|
+
gem "rspec-rails", "~> 8.0"
|
|
22
22
|
gem "factory_bot_rails", "~> 6.4"
|
|
23
23
|
gem "faker", "~> 3.5"
|
|
24
|
-
gem "shoulda-matchers", "~>
|
|
25
|
-
gem "simplecov", "~>
|
|
24
|
+
gem "shoulda-matchers", "~> 8.0", ">= 8.0.1"
|
|
25
|
+
gem "simplecov", "~> 1.3", ">= 1.3.2", require: false
|
|
26
26
|
gem "webmock", "~> 3.24"
|
|
27
27
|
gem "vcr", "~> 6.3"
|
|
28
28
|
|
|
@@ -37,20 +37,20 @@ group :development, :test do
|
|
|
37
37
|
gem "mutant-rspec", "~> 0.12", require: false
|
|
38
38
|
|
|
39
39
|
# Security
|
|
40
|
-
gem "brakeman", "~>
|
|
40
|
+
gem "brakeman", "~> 8.1", require: false
|
|
41
41
|
gem "bundler-audit", "~> 0.9", require: false
|
|
42
42
|
|
|
43
43
|
# Documentation
|
|
44
44
|
gem "yard", "~> 0.9", require: false
|
|
45
45
|
|
|
46
46
|
# Database Quality
|
|
47
|
-
gem "database_consistency", "~>
|
|
47
|
+
gem "database_consistency", "~> 3.0", require: false
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
group :development do
|
|
51
51
|
# Performance
|
|
52
52
|
gem "bullet", "~> 8.0"
|
|
53
|
-
gem "rack-mini-profiler", "~>
|
|
53
|
+
gem "rack-mini-profiler", "~> 5.0"
|
|
54
54
|
|
|
55
55
|
# Git Hooks
|
|
56
56
|
gem "lefthook", require: false
|
|
@@ -9,21 +9,23 @@ thresholds = if File.exist?(thresholds_path)
|
|
|
9
9
|
{ 'line' => 80, 'branch' => 70 }
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
SimpleCov.
|
|
13
|
-
enable_coverage :branch
|
|
12
|
+
SimpleCov.load_profile 'rails'
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
SimpleCov.configure do
|
|
15
|
+
deprecations :raise
|
|
16
|
+
coverage :line, minimum: thresholds['line']
|
|
17
|
+
coverage :branch, minimum: thresholds['branch']
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
19
|
+
group 'Models', 'app/models'
|
|
20
|
+
group 'Controllers', 'app/controllers'
|
|
21
|
+
group 'Services', 'app/services'
|
|
22
|
+
group 'Jobs', 'app/jobs'
|
|
23
|
+
group 'Mailers', 'app/mailers'
|
|
24
|
+
group 'Serializers', 'app/serializers'
|
|
25
|
+
group 'Libraries', 'lib'
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
skip '/spec/'
|
|
28
|
+
skip '/config/'
|
|
29
|
+
skip '/db/'
|
|
30
|
+
skip '/vendor/'
|
|
29
31
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
|
|
2
3
|
# Seeded by Lisa on first setup — this file is YOURS.
|
|
3
4
|
# Lisa will not overwrite it. (copy-overwrite assets ARE replaced each run.)
|
|
4
5
|
|
|
5
6
|
require 'simplecov'
|
|
7
|
+
SimpleCov.start
|
|
6
8
|
|
|
7
9
|
RSpec.configure do |config|
|
|
8
10
|
config.expect_with :rspec do |expectations|
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Ordinary installed compatibility smoke checks for the dependency refresh. */
|
|
3
|
+
import assert from "node:assert/strict";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
import {
|
|
9
|
+
SCRATCH_SUPERVISION_LEASE_ENV,
|
|
10
|
+
createSupervisedWorkerScope,
|
|
11
|
+
parseScratchSupervisionLease,
|
|
12
|
+
removeSupervisedWorkerScope,
|
|
13
|
+
} from "../dist/configs/vitest/scratch-supervision.js";
|
|
14
|
+
|
|
15
|
+
const root = path.resolve(process.env.LISA_NPM_PROOF_ROOT ?? ".");
|
|
16
|
+
const packageFile = "package.json";
|
|
17
|
+
const require = createRequire(path.join(root, packageFile));
|
|
18
|
+
const serializedLease = process.env[SCRATCH_SUPERVISION_LEASE_ENV];
|
|
19
|
+
assert.ok(serializedLease, "Run through lisa-test-run with its owned lease");
|
|
20
|
+
const lease = parseScratchSupervisionLease(serializedLease);
|
|
21
|
+
const scope = createSupervisedWorkerScope(lease);
|
|
22
|
+
const temporary = scope.path;
|
|
23
|
+
const checks = [];
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const j = require("jscodeshift").withParser("tsx");
|
|
27
|
+
const tree = j("const value: number = 1; const view = <div>{value}</div>;");
|
|
28
|
+
tree.find(j.Identifier, { name: "value" }).forEach(p => {
|
|
29
|
+
p.node.name = "answer";
|
|
30
|
+
});
|
|
31
|
+
assert.ok(tree.toSource().includes("{answer}"));
|
|
32
|
+
checks.push("jscodeshift: TSX transform");
|
|
33
|
+
|
|
34
|
+
const eslintRequire = createRequire(require.resolve("eslint"));
|
|
35
|
+
const Ajv6 = eslintRequire("ajv");
|
|
36
|
+
const validate6 = new Ajv6().compile({
|
|
37
|
+
type: "object",
|
|
38
|
+
required: ["name"],
|
|
39
|
+
properties: { name: { type: "string" } },
|
|
40
|
+
});
|
|
41
|
+
assert.equal(validate6({ name: "ordinary" }), true);
|
|
42
|
+
assert.equal(validate6({ name: 1 }), false);
|
|
43
|
+
const commitlintRequire = createRequire(require.resolve("@commitlint/cli"));
|
|
44
|
+
const Ajv8 = createRequire(
|
|
45
|
+
commitlintRequire.resolve("@commitlint/config-validator")
|
|
46
|
+
)("ajv");
|
|
47
|
+
const validate8 = new Ajv8.default().compile({ type: "integer" });
|
|
48
|
+
assert.equal(validate8(42), true);
|
|
49
|
+
assert.equal(validate8("42"), false);
|
|
50
|
+
checks.push("Ajv: distinct v6/v8 schema APIs");
|
|
51
|
+
|
|
52
|
+
const legacyMatch = eslintRequire("minimatch");
|
|
53
|
+
assert.equal(typeof legacyMatch, "function");
|
|
54
|
+
assert.equal(legacyMatch("src/file.ts", "**/*.ts"), true);
|
|
55
|
+
assert.equal(legacyMatch("src/file.js", "**/*.ts"), false);
|
|
56
|
+
const modernMatch = require("minimatch").minimatch;
|
|
57
|
+
assert.equal(modernMatch("src/file.ts", "**/*.ts"), true);
|
|
58
|
+
assert.equal(modernMatch("src/file.js", "**/*.ts"), false);
|
|
59
|
+
checks.push("minimatch: legacy callable and modern object APIs");
|
|
60
|
+
|
|
61
|
+
const strykerRequire = createRequire(
|
|
62
|
+
require.resolve("@stryker-mutator/core")
|
|
63
|
+
);
|
|
64
|
+
const restRequire = createRequire(
|
|
65
|
+
strykerRequire.resolve("typed-rest-client/HttpClient")
|
|
66
|
+
);
|
|
67
|
+
const qs = restRequire("qs");
|
|
68
|
+
assert.deepEqual(qs.parse("name=ordinary&tasks=lint&tasks=test"), {
|
|
69
|
+
name: "ordinary",
|
|
70
|
+
tasks: ["lint", "test"],
|
|
71
|
+
});
|
|
72
|
+
assert.equal(qs.stringify({ name: "ordinary" }), "name=ordinary");
|
|
73
|
+
assert.equal(
|
|
74
|
+
typeof strykerRequire("@stryker-mutator/core").Stryker,
|
|
75
|
+
"function"
|
|
76
|
+
);
|
|
77
|
+
checks.push(
|
|
78
|
+
"Stryker: installed core and compatible qs6 parse/stringify APIs"
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
const functionalPath = require.resolve("eslint-plugin-functional");
|
|
82
|
+
const functional = (await import(pathToFileURL(functionalPath).href)).default;
|
|
83
|
+
const functionalRequire = createRequire(functionalPath);
|
|
84
|
+
const { deepmerge } = functionalRequire("deepmerge-ts");
|
|
85
|
+
assert.deepEqual(
|
|
86
|
+
deepmerge(
|
|
87
|
+
{ labels: ["first"], options: { enabled: true } },
|
|
88
|
+
{ labels: ["second"], options: { level: 2 } }
|
|
89
|
+
),
|
|
90
|
+
{
|
|
91
|
+
labels: ["first", "second"],
|
|
92
|
+
options: { enabled: true, level: 2 },
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
const { Linter } = require("eslint");
|
|
96
|
+
const linter = new Linter();
|
|
97
|
+
const functionalConfig = [
|
|
98
|
+
{
|
|
99
|
+
plugins: { functional },
|
|
100
|
+
rules: {
|
|
101
|
+
"functional/no-let": ["error", { ignoreIdentifierPattern: "^ignored" }],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
const ordinaryLet = linter.verify(
|
|
106
|
+
"let answer = 42; answer;",
|
|
107
|
+
functionalConfig
|
|
108
|
+
);
|
|
109
|
+
assert.equal(
|
|
110
|
+
ordinaryLet.filter(message => message.ruleId === "functional/no-let")
|
|
111
|
+
.length,
|
|
112
|
+
1
|
|
113
|
+
);
|
|
114
|
+
assert.deepEqual(
|
|
115
|
+
linter.verify("const answer = 42; answer;", functionalConfig),
|
|
116
|
+
[]
|
|
117
|
+
);
|
|
118
|
+
checks.push("functional: actual ESLint rule and installed deepmerge APIs");
|
|
119
|
+
|
|
120
|
+
const cdk = require("aws-cdk-lib");
|
|
121
|
+
const app = new cdk.App({ outdir: path.join(temporary, "cdk.out") });
|
|
122
|
+
const stack = new cdk.Stack(app, "AnonymousProof");
|
|
123
|
+
new cdk.aws_s3.Bucket(stack, "Storage", {
|
|
124
|
+
enforceSSL: true,
|
|
125
|
+
versioned: true,
|
|
126
|
+
blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL,
|
|
127
|
+
});
|
|
128
|
+
new cdk.CfnOutput(stack, "Proof", { value: "synth-only" });
|
|
129
|
+
const template = app.synth().getStackArtifact(stack.artifactId).template;
|
|
130
|
+
assert.equal(template.Outputs.Proof.Value, "synth-only");
|
|
131
|
+
assert.ok(
|
|
132
|
+
Object.values(template.Resources).some(
|
|
133
|
+
resource => resource.Type === "AWS::S3::Bucket"
|
|
134
|
+
)
|
|
135
|
+
);
|
|
136
|
+
checks.push("CDK: actual local synthesis without cloud operations");
|
|
137
|
+
|
|
138
|
+
const release = path.join(temporary, "release");
|
|
139
|
+
fs.mkdirSync(release);
|
|
140
|
+
fs.writeFileSync(
|
|
141
|
+
path.join(release, packageFile),
|
|
142
|
+
JSON.stringify({ name: "anonymous-release-proof", version: "1.0.0" })
|
|
143
|
+
);
|
|
144
|
+
const original = process.cwd();
|
|
145
|
+
process.chdir(release);
|
|
146
|
+
try {
|
|
147
|
+
await require("standard-version")({
|
|
148
|
+
dryRun: true,
|
|
149
|
+
releaseAs: "patch",
|
|
150
|
+
skip: { changelog: true, commit: true, tag: true },
|
|
151
|
+
silent: true,
|
|
152
|
+
});
|
|
153
|
+
assert.equal(
|
|
154
|
+
JSON.parse(fs.readFileSync(packageFile, "utf8")).version,
|
|
155
|
+
"1.0.0"
|
|
156
|
+
);
|
|
157
|
+
} finally {
|
|
158
|
+
process.chdir(original);
|
|
159
|
+
}
|
|
160
|
+
checks.push(
|
|
161
|
+
"standard-version: ordinary release dry run leaves manifest unchanged"
|
|
162
|
+
);
|
|
163
|
+
console.log(JSON.stringify({ success: true, checks }, null, 2));
|
|
164
|
+
} finally {
|
|
165
|
+
removeSupervisedWorkerScope(scope, lease);
|
|
166
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Run with the consumer bundle, Ruby 3.4, and an empty owned scratch directory:
|
|
4
|
+
# bundle exec ruby scripts/verify-rails-tooling.rb MODE TEMPLATE_ROOT SCRATCH
|
|
5
|
+
# Modes: bounds, configuration, coverage, coverage-negative,
|
|
6
|
+
# coverage-line-negative, matchers, matchers-negative, security,
|
|
7
|
+
# security-negative, lint, lint-negative, helper-lint.
|
|
8
|
+
# This bounded probe never boots Rails or connects to a database. The packaged
|
|
9
|
+
# Rails/MySQL journey remains a separate acceptance check.
|
|
10
|
+
require "fileutils"
|
|
11
|
+
require "json"
|
|
12
|
+
|
|
13
|
+
SELECTED = {
|
|
14
|
+
"brakeman" => "8.1.0",
|
|
15
|
+
"database_consistency" => "3.0.14",
|
|
16
|
+
"rack-mini-profiler" => "5.0.0",
|
|
17
|
+
"rspec-rails" => "8.0.4",
|
|
18
|
+
"rubocop-capybara" => "3.0.0",
|
|
19
|
+
"rubocop-yard" => "1.3.0",
|
|
20
|
+
"shoulda-matchers" => "8.0.1",
|
|
21
|
+
"simplecov" => "1.3.2"
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
mode, template_root, scratch = ARGV
|
|
25
|
+
abort "Expected MODE TEMPLATE_ROOT SCRATCH" unless ARGV.length == 3
|
|
26
|
+
template_root = File.realpath(template_root)
|
|
27
|
+
scratch = File.realpath(scratch)
|
|
28
|
+
abort "Scratch must be empty" unless Dir.children(scratch).empty?
|
|
29
|
+
|
|
30
|
+
def copy_template(root, relative, destination)
|
|
31
|
+
FileUtils.mkdir_p(File.dirname(destination))
|
|
32
|
+
FileUtils.cp(File.join(root, relative), destination)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Dir.chdir(scratch) do
|
|
36
|
+
case mode
|
|
37
|
+
when "bounds"
|
|
38
|
+
require "bundler"
|
|
39
|
+
definition = Bundler::Dsl.evaluate(File.join(template_root, "rails/copy-overwrite/Gemfile.lisa"), nil, {})
|
|
40
|
+
failures = SELECTED.filter_map do |name, selected|
|
|
41
|
+
dependency = definition.dependencies.find { |item| item.name == name }
|
|
42
|
+
abort "Missing family #{name}" unless dependency
|
|
43
|
+
actual = Gem.loaded_specs.fetch(name).version
|
|
44
|
+
abort "Wrong selected version #{name}: #{actual}" unless actual.to_s == selected
|
|
45
|
+
next_major = Gem::Version.new("#{actual.segments.first + 1}.0.0")
|
|
46
|
+
abort "Untested next major admitted for #{name}" if dependency.requirement.satisfied_by?(next_major)
|
|
47
|
+
"#{name}: #{dependency.requirement} rejects #{actual}" unless dependency.requirement.satisfied_by?(actual)
|
|
48
|
+
end
|
|
49
|
+
abort failures.join("\n") unless failures.empty?
|
|
50
|
+
{ "shoulda-matchers" => "8.0.0", "simplecov" => "1.3.1" }.each do |name, old_patch|
|
|
51
|
+
requirement = definition.dependencies.find { |item| item.name == name }.requirement
|
|
52
|
+
abort "Pre-fix patch admitted for #{name}" if requirement.satisfied_by?(Gem::Version.new(old_patch))
|
|
53
|
+
end
|
|
54
|
+
puts JSON.generate(marker: "RAILS_TOOL_BOUNDS_ACCEPTED", versions: SELECTED)
|
|
55
|
+
when "configuration"
|
|
56
|
+
copy_template(template_root, "rails/create-only/.simplecov", ".simplecov")
|
|
57
|
+
require "simplecov/no_defaults"
|
|
58
|
+
require "simplecov"
|
|
59
|
+
SimpleCov.deprecations :raise
|
|
60
|
+
load ".simplecov"
|
|
61
|
+
abort "Configuration must not start coverage" if Coverage.running?
|
|
62
|
+
puts "SIMPLECOV_CONFIGURATION_ACCEPTED"
|
|
63
|
+
when "coverage", "coverage-negative", "coverage-line-negative"
|
|
64
|
+
copy_template(template_root, "rails/create-only/.simplecov", ".simplecov")
|
|
65
|
+
copy_template(template_root, "rails/create-only/spec/spec_helper.rb", "spec/spec_helper.rb")
|
|
66
|
+
FileUtils.mkdir_p("app/models")
|
|
67
|
+
File.write("app/models/coverage_probe.rb", <<~RUBY)
|
|
68
|
+
class CoverageProbe
|
|
69
|
+
def greeting(value)
|
|
70
|
+
if value == :hello
|
|
71
|
+
"welcome"
|
|
72
|
+
else
|
|
73
|
+
"goodbye"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
def detail
|
|
77
|
+
text = "detail"
|
|
78
|
+
text.upcase
|
|
79
|
+
text.reverse
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
RUBY
|
|
83
|
+
require "rspec/core"
|
|
84
|
+
load "spec/spec_helper.rb"
|
|
85
|
+
SimpleCov.deprecations :raise
|
|
86
|
+
abort "Coverage must start in the helper" unless Coverage.running?
|
|
87
|
+
SimpleCov.merging false
|
|
88
|
+
require File.join(scratch, "app/models/coverage_probe")
|
|
89
|
+
probe = CoverageProbe.new
|
|
90
|
+
abort "Wrong exercised result" unless probe.greeting(:hello) == "welcome"
|
|
91
|
+
if mode != "coverage-negative"
|
|
92
|
+
abort "Wrong second branch" unless probe.greeting(:other) == "goodbye"
|
|
93
|
+
end
|
|
94
|
+
abort "Wrong line-control result" unless probe.detail == "liated" if mode == "coverage"
|
|
95
|
+
puts "COVERAGE_PROBE_EXECUTED"
|
|
96
|
+
when "matchers", "matchers-negative"
|
|
97
|
+
copy_template(template_root, "rails/create-only/.simplecov", ".simplecov")
|
|
98
|
+
copy_template(template_root, "rails/create-only/spec/spec_helper.rb", "spec/spec_helper.rb")
|
|
99
|
+
FileUtils.mkdir_p("app/models")
|
|
100
|
+
FileUtils.mkdir_p("spec/models")
|
|
101
|
+
File.write("app/models/matcher_probe.rb", <<~RUBY)
|
|
102
|
+
class MatcherProbe
|
|
103
|
+
include ActiveModel::Model
|
|
104
|
+
attr_accessor :name
|
|
105
|
+
#{'validates :name, presence: true' if mode == "matchers"}
|
|
106
|
+
end
|
|
107
|
+
RUBY
|
|
108
|
+
File.write("spec/models/matcher_probe_spec.rb", <<~RUBY)
|
|
109
|
+
require "spec_helper"
|
|
110
|
+
require "active_model"
|
|
111
|
+
require "shoulda/matchers"
|
|
112
|
+
Shoulda::Matchers.configure do |config|
|
|
113
|
+
config.integrate do |with|
|
|
114
|
+
with.test_framework :rspec
|
|
115
|
+
with.library :active_model
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
require File.join(Dir.pwd, "app/models/matcher_probe")
|
|
119
|
+
RSpec.describe MatcherProbe, type: :model do
|
|
120
|
+
it { is_expected.to validate_presence_of(:name) }
|
|
121
|
+
end
|
|
122
|
+
RUBY
|
|
123
|
+
require "rspec/core"
|
|
124
|
+
exit RSpec::Core::Runner.run(["--format", "documentation", "spec/models/matcher_probe_spec.rb"])
|
|
125
|
+
when "security", "security-negative"
|
|
126
|
+
FileUtils.mkdir_p(["app/controllers", "app/models", "config"])
|
|
127
|
+
File.write("Gemfile", "source 'https://rubygems.org'\ngem 'rails', '= 8.1.4'\n")
|
|
128
|
+
File.write("config/application.rb", "require 'rails/all'\nmodule ToolingProbe\nclass Application < Rails::Application\nconfig.load_defaults 8.1\nend\nend\n")
|
|
129
|
+
File.write("config/routes.rb", "Rails.application.routes.draw { resources :probe_records }\n")
|
|
130
|
+
File.write("app/controllers/application_controller.rb", "class ApplicationController < ActionController::Base\nend\n")
|
|
131
|
+
File.write("app/models/probe_record.rb", "class ProbeRecord < ActiveRecord::Base\nend\n")
|
|
132
|
+
query = if mode == "security-negative"
|
|
133
|
+
'ProbeRecord.where("name = \'#{params[:name]}\'")'
|
|
134
|
+
else
|
|
135
|
+
"ProbeRecord.where(name: params[:name])"
|
|
136
|
+
end
|
|
137
|
+
File.write("app/controllers/probe_records_controller.rb", "class ProbeRecordsController < ApplicationController\n def index\n #{query}\n end\nend\n")
|
|
138
|
+
require "brakeman"
|
|
139
|
+
tracker = Brakeman.run(app_path: scratch, quiet: true, print_report: false, run_checks: ["CheckSQL"])
|
|
140
|
+
abort "No SQL check executed" unless tracker.checks.checks_run.include?("SQL")
|
|
141
|
+
abort "No model/controller inspected" if tracker.models.empty? || tracker.controllers.empty?
|
|
142
|
+
warnings = tracker.checks.all_warnings.select { |warning| warning.warning_type == "SQL Injection" }
|
|
143
|
+
puts JSON.generate(marker: "BRAKEMAN_SQL_CONTROL", checks: tracker.checks.checks_run, warnings: warnings.length, models: tracker.models.length, controllers: tracker.controllers.length)
|
|
144
|
+
exit(warnings.empty? ? 0 : 1)
|
|
145
|
+
when "helper-lint"
|
|
146
|
+
copy_template(template_root, "rails/copy-overwrite/.rubocop.yml", ".rubocop.yml")
|
|
147
|
+
[".rubocop_todo.yml", "rubocop.thresholds.yml", ".rubocop.local.yml"].each do |name|
|
|
148
|
+
copy_template(template_root, "rails/create-only/#{name}", name)
|
|
149
|
+
end
|
|
150
|
+
["spec/spec_helper.rb", "spec/rails_helper.rb"].each do |name|
|
|
151
|
+
copy_template(template_root, "rails/create-only/#{name}", name)
|
|
152
|
+
end
|
|
153
|
+
require "rubocop"
|
|
154
|
+
exit RuboCop::CLI.new.run(["--format", "json", "--cache", "false", "spec/spec_helper.rb", "spec/rails_helper.rb"])
|
|
155
|
+
when "lint", "lint-negative"
|
|
156
|
+
copy_template(template_root, "rails/copy-overwrite/.rubocop.yml", ".rubocop.yml")
|
|
157
|
+
[".rubocop_todo.yml", "rubocop.thresholds.yml", ".rubocop.local.yml"].each do |name|
|
|
158
|
+
copy_template(template_root, "rails/create-only/#{name}", name)
|
|
159
|
+
end
|
|
160
|
+
FileUtils.mkdir_p("spec/features")
|
|
161
|
+
negative = mode == "lint-negative"
|
|
162
|
+
File.write("spec/features/tooling_probe_spec.rb", <<~RUBY)
|
|
163
|
+
#{negative ? 'all(".thing").first' : 'find(".thing")'}
|
|
164
|
+
|
|
165
|
+
# @param #{negative ? "wrong" : "name"} [String] the name
|
|
166
|
+
def greeting(name)
|
|
167
|
+
name
|
|
168
|
+
end
|
|
169
|
+
RUBY
|
|
170
|
+
require "rubocop"
|
|
171
|
+
exit RuboCop::CLI.new.run(["--only", "Capybara/FindAllFirst,YARD/MismatchName", "--format", "json", "--cache", "false", "spec/features/tooling_probe_spec.rb"])
|
|
172
|
+
else
|
|
173
|
+
abort "Unknown mode #{mode}"
|
|
174
|
+
end
|
|
175
|
+
end
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./node_modules/@stryker-mutator/core/schema/stryker-schema.json",
|
|
3
3
|
"testRunner": "vitest",
|
|
4
|
+
"_pluginsComment": "Resolve the declared runner explicitly for isolated dependency layouts instead of relying on sibling package discovery.",
|
|
5
|
+
"plugins": ["@stryker-mutator/vitest-runner"],
|
|
4
6
|
"reporters": ["clear-text", "progress", "html"],
|
|
5
7
|
"coverageAnalysis": "perTest",
|
|
6
8
|
"ignoreStatic": true,
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"typecheck:lisa": "node scripts/lib/worktree-dependencies.mjs && tsc --noEmit"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
|
-
"eslint-plugin-oxlint": "
|
|
35
|
-
"oxlint": "
|
|
34
|
+
"eslint-plugin-oxlint": "1.62.0",
|
|
35
|
+
"oxlint": "1.62.0",
|
|
36
36
|
"oxlint-tsgolint": "^0.24.0",
|
|
37
37
|
"@stryker-mutator/core": "^9.0.0",
|
|
38
38
|
"@stryker-mutator/vitest-runner": "^9.0.0",
|
|
@@ -49,7 +49,9 @@
|
|
|
49
49
|
"vite": "^8.3.2",
|
|
50
50
|
"ws": ">=8.21.0",
|
|
51
51
|
"form-data": ">=4.0.6",
|
|
52
|
-
"systeminformation": ">=5.31.7"
|
|
52
|
+
"systeminformation": ">=5.31.7",
|
|
53
|
+
"qs": "^6.16.0",
|
|
54
|
+
"deepmerge-ts": "^8.0.1"
|
|
53
55
|
},
|
|
54
56
|
"overrides": {
|
|
55
57
|
"@isaacs/brace-expansion": "^5.0.1",
|
|
@@ -62,16 +64,18 @@
|
|
|
62
64
|
"vite": "^8.3.2",
|
|
63
65
|
"ws": ">=8.21.0",
|
|
64
66
|
"form-data": ">=4.0.6",
|
|
65
|
-
"systeminformation": ">=5.31.7"
|
|
67
|
+
"systeminformation": ">=5.31.7",
|
|
68
|
+
"qs": "^6.16.0",
|
|
69
|
+
"deepmerge-ts": "^8.0.1"
|
|
66
70
|
}
|
|
67
71
|
},
|
|
68
72
|
"defaults": {
|
|
69
73
|
"devDependencies": {
|
|
70
74
|
"@ast-grep/cli": "^0.40.4",
|
|
71
75
|
"@vitest/coverage-v8": "^4.1.11",
|
|
72
|
-
"eslint": "^9.39.
|
|
76
|
+
"eslint": "^9.39.5",
|
|
73
77
|
"husky": "^8.0.0",
|
|
74
|
-
"prettier": "
|
|
78
|
+
"prettier": "3.8.0",
|
|
75
79
|
"typescript": "^6.0.3",
|
|
76
80
|
"vitest": "^4.1.11"
|
|
77
81
|
},
|