repokeeper 0.3.0 → 0.4.1
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/README.md +19 -4
- package/dist/config/types.js +13 -1
- package/dist/platforms/github.js +10 -0
- package/dist/stacks/go.js +30 -0
- package/dist/stacks/gradle.js +16 -0
- package/dist/stacks/index.js +14 -1
- package/dist/stacks/java.js +2 -5
- package/dist/stacks/kotlin.js +54 -0
- package/dist/stacks/php.js +53 -0
- package/dist/stacks/python.js +3 -7
- package/dist/stacks/ruby.js +53 -0
- package/dist/stacks/rust.js +39 -0
- package/dist/stacks/support.js +4 -0
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/templates/gitignore/Composer.gitignore +6 -0
- package/templates/gitignore/Go.gitignore +32 -0
- package/templates/gitignore/Kotlin.gitignore +27 -0
- package/templates/gitignore/Ruby.gitignore +56 -0
- package/templates/gitignore/Rust.gitignore +24 -0
package/README.md
CHANGED
|
@@ -5,8 +5,9 @@ files, editor and gitignore settings, Dependabot, CI and releases — applied on
|
|
|
5
5
|
the standard evolves.
|
|
6
6
|
|
|
7
7
|
> Status: early development. Supported stacks: Node.js (including NestJS), Python, Dart and
|
|
8
|
-
> Flutter, shell and PowerShell scripts, Java (Maven and Gradle)
|
|
9
|
-
> GitHub settings
|
|
8
|
+
> Flutter, shell and PowerShell scripts, Java (Maven and Gradle), Kotlin, .NET, Go, Rust, PHP and
|
|
9
|
+
> Ruby, each with CI and releases, plus GitHub settings through `repokeeper github apply`. See the
|
|
10
|
+
> [design](docs/superpowers/specs/2026-09-25-repokeeper-design.md).
|
|
10
11
|
|
|
11
12
|
## Usage
|
|
12
13
|
|
|
@@ -51,8 +52,10 @@ Requires Node.js 22.12 or newer.
|
|
|
51
52
|
|
|
52
53
|
`ci.yml` and `release.yml` call reusable workflows from this repository (`stack-node.yml`,
|
|
53
54
|
`commitlint.yml`, `release-please.yml`) at the moving major tag, so fixes reach every repository
|
|
54
|
-
without a pull request. repokeeper owns the `name`, `on` and `
|
|
55
|
-
adds; jobs you add yourself are left alone, and so is the formatting of the rest of the
|
|
55
|
+
without a pull request. repokeeper owns the `name`, `on`, `permissions` and `concurrency` keys and
|
|
56
|
+
the jobs it adds; jobs you add yourself are left alone, and so is the formatting of the rest of the
|
|
57
|
+
file. A new push to a pull request cancels that pull request's earlier `ci` run; runs on the default
|
|
58
|
+
branch always finish.
|
|
56
59
|
|
|
57
60
|
The script stack runs ShellCheck and `shfmt -d` on `*.sh` (format with `shfmt -w` before pushing)
|
|
58
61
|
and PSScriptAnalyzer on `*.ps1`, which fails on errors and warnings (not on information-level rules). To
|
|
@@ -104,6 +107,18 @@ It prints every change first and applies them with `--yes`, or after you confirm
|
|
|
104
107
|
access to the repository. Legacy branch protection, visibility, secrets and collaborators are never
|
|
105
108
|
touched.
|
|
106
109
|
|
|
110
|
+
## Pilots
|
|
111
|
+
|
|
112
|
+
Each stack is tried on a real or demo repository before a release. The demo repositories carry the
|
|
113
|
+
[`repokeeper-demo`](https://github.com/topics/repokeeper-demo) topic:
|
|
114
|
+
[dart](https://github.com/vannt-dev/repokeeper-demo-dart),
|
|
115
|
+
[dotnet](https://github.com/vannt-dev/repokeeper-demo-dotnet),
|
|
116
|
+
[go](https://github.com/vannt-dev/repokeeper-demo-go),
|
|
117
|
+
[kotlin](https://github.com/vannt-dev/repokeeper-demo-kotlin),
|
|
118
|
+
[php](https://github.com/vannt-dev/repokeeper-demo-php),
|
|
119
|
+
[ruby](https://github.com/vannt-dev/repokeeper-demo-ruby) and
|
|
120
|
+
[rust](https://github.com/vannt-dev/repokeeper-demo-rust).
|
|
121
|
+
|
|
107
122
|
## License
|
|
108
123
|
|
|
109
124
|
MIT
|
package/dist/config/types.js
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
export const STACK_IDS = [
|
|
1
|
+
export const STACK_IDS = [
|
|
2
|
+
"node",
|
|
3
|
+
"python",
|
|
4
|
+
"dart",
|
|
5
|
+
"script",
|
|
6
|
+
"java",
|
|
7
|
+
"dotnet",
|
|
8
|
+
"go",
|
|
9
|
+
"rust",
|
|
10
|
+
"kotlin",
|
|
11
|
+
"php",
|
|
12
|
+
"ruby",
|
|
13
|
+
];
|
|
2
14
|
/** The branch workflows run on and contributors branch from: `github.default_branch`, or `main`. */
|
|
3
15
|
export function defaultBranch(config) {
|
|
4
16
|
const branch = config.github?.default_branch;
|
package/dist/platforms/github.js
CHANGED
|
@@ -99,6 +99,8 @@ export const githubPlatform = {
|
|
|
99
99
|
directory: "/",
|
|
100
100
|
schedule: { interval: "weekly" },
|
|
101
101
|
groups: { [`${ecosystem}-minor-and-patch`]: { "update-types": ["minor", "patch"] } },
|
|
102
|
+
// Dependabot infers a Conventional Commits prefix only from history; young repositories fail commitlint
|
|
103
|
+
"commit-message": { prefix: "chore", include: "scope" },
|
|
102
104
|
// @types/node majors track the Node.js line a project runs on, which the project chooses
|
|
103
105
|
...(ecosystem === "npm"
|
|
104
106
|
? { ignore: [{ "dependency-name": "@types/node", "update-types": ["version-update:semver-major"] }] }
|
|
@@ -132,6 +134,13 @@ export const githubPlatform = {
|
|
|
132
134
|
workflowKey("ci", path, ["name"], "ci"),
|
|
133
135
|
workflowKey("ci", path, ["on"], { pull_request: null, push: { branches: [defaultBranch(ctx.config)] } }),
|
|
134
136
|
workflowKey("ci", path, ["permissions"], { contents: "read" }),
|
|
137
|
+
// a new push to a pull request makes its earlier run pointless; runs on the default branch always finish
|
|
138
|
+
workflowKey("ci", path, ["concurrency"], {
|
|
139
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: GitHub Actions expressions
|
|
140
|
+
group: "${{ github.workflow }}-${{ github.ref }}",
|
|
141
|
+
// biome-ignore lint/suspicious/noTemplateCurlyInString: GitHub Actions expressions
|
|
142
|
+
"cancel-in-progress": "${{ github.event_name == 'pull_request' }}",
|
|
143
|
+
}),
|
|
135
144
|
...jobs,
|
|
136
145
|
];
|
|
137
146
|
},
|
|
@@ -151,6 +160,7 @@ export const githubPlatform = {
|
|
|
151
160
|
"bump-minor-pre-major": true,
|
|
152
161
|
"include-component-in-tag": false,
|
|
153
162
|
...(release.extraFiles && release.extraFiles.length > 0 ? { "extra-files": release.extraFiles } : {}),
|
|
163
|
+
...(release.versionFile ? { "version-file": release.versionFile } : {}),
|
|
154
164
|
},
|
|
155
165
|
},
|
|
156
166
|
}),
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { checkKeys, stringList } from "./support.js";
|
|
4
|
+
const OPTION_KEYS = ["versions", "os"];
|
|
5
|
+
const GOFMT = 'test -z "$(gofmt -l .)" || { gofmt -l .; exit 1; }';
|
|
6
|
+
export const goStack = {
|
|
7
|
+
id: "go",
|
|
8
|
+
detect: (root) => existsSync(join(root, "go.mod")),
|
|
9
|
+
async resolve(_root, options = {}) {
|
|
10
|
+
checkKeys("go", options, OPTION_KEYS);
|
|
11
|
+
return {
|
|
12
|
+
id: "go",
|
|
13
|
+
staged: [{ name: "go:gofmt", glob: "*.go", run: "gofmt -w {staged_files}" }],
|
|
14
|
+
test: "go test ./...",
|
|
15
|
+
install: "go mod download",
|
|
16
|
+
gitignore: ["Go"],
|
|
17
|
+
dependabot: ["gomod"],
|
|
18
|
+
ci: {
|
|
19
|
+
workflow: "stack-go.yml",
|
|
20
|
+
with: {
|
|
21
|
+
"go-versions": JSON.stringify(stringList("go", options, "versions") ?? ["stable"]),
|
|
22
|
+
os: JSON.stringify(stringList("go", options, "os") ?? ["ubuntu-latest"]),
|
|
23
|
+
commands: JSON.stringify([GOFMT, "go vet ./...", "go test ./..."]),
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
// Go modules are versioned by their tags; the manifest starts from the latest one
|
|
27
|
+
release: { type: "go", version: null },
|
|
28
|
+
};
|
|
29
|
+
},
|
|
30
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { readText } from "./support.js";
|
|
4
|
+
export const GRADLE_FILES = ["build.gradle", "build.gradle.kts"];
|
|
5
|
+
/** The build command (wrapper when present) and release info of a Gradle project. */
|
|
6
|
+
export async function gradleBuild(root) {
|
|
7
|
+
const wrapper = existsSync(join(root, "gradlew"));
|
|
8
|
+
const properties = await readText(root, "gradle.properties");
|
|
9
|
+
const version = properties ? (/^version\s*=\s*(\S+)/m.exec(properties)?.[1] ?? null) : null;
|
|
10
|
+
return {
|
|
11
|
+
command: wrapper ? "./gradlew check" : "gradle check",
|
|
12
|
+
wrapper,
|
|
13
|
+
// release-please's generic updater changes gradle.properties once it carries x-release-please markers
|
|
14
|
+
release: { type: "simple", version, ...(properties !== null ? { extraFiles: ["gradle.properties"] } : {}) },
|
|
15
|
+
};
|
|
16
|
+
}
|
package/dist/stacks/index.js
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { STACK_IDS } from "../config/types.js";
|
|
2
2
|
import { dartStack } from "./dart.js";
|
|
3
3
|
import { dotnetStack } from "./dotnet.js";
|
|
4
|
+
import { goStack } from "./go.js";
|
|
4
5
|
import { javaStack } from "./java.js";
|
|
6
|
+
import { kotlinStack } from "./kotlin.js";
|
|
5
7
|
import { nodeStack } from "./node.js";
|
|
8
|
+
import { phpStack } from "./php.js";
|
|
6
9
|
import { pythonStack } from "./python.js";
|
|
10
|
+
import { rubyStack } from "./ruby.js";
|
|
11
|
+
import { rustStack } from "./rust.js";
|
|
7
12
|
import { scriptStack } from "./script.js";
|
|
8
13
|
const PACKS = {
|
|
9
14
|
node: nodeStack,
|
|
@@ -12,12 +17,20 @@ const PACKS = {
|
|
|
12
17
|
script: scriptStack,
|
|
13
18
|
java: javaStack,
|
|
14
19
|
dotnet: dotnetStack,
|
|
20
|
+
go: goStack,
|
|
21
|
+
rust: rustStack,
|
|
22
|
+
kotlin: kotlinStack,
|
|
23
|
+
php: phpStack,
|
|
24
|
+
ruby: rubyStack,
|
|
15
25
|
};
|
|
16
26
|
export function getStackPack(id) {
|
|
17
27
|
return PACKS[id];
|
|
18
28
|
}
|
|
19
29
|
export async function detectStacks(root) {
|
|
20
|
-
|
|
30
|
+
let found = STACK_IDS.filter((id) => PACKS[id].detect(root));
|
|
31
|
+
// a Kotlin build is also a Gradle build; one CI job runs it
|
|
32
|
+
if (found.includes("kotlin"))
|
|
33
|
+
found = found.filter((id) => id !== "java");
|
|
21
34
|
// scripts beside another stack belong to that stack; the script pack is for script-only repositories
|
|
22
35
|
return found.length > 1 ? found.filter((id) => id !== "script") : found;
|
|
23
36
|
}
|
package/dist/stacks/java.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
+
import { gradleBuild } from "./gradle.js";
|
|
3
4
|
import { checkKeys, readText, stringList } from "./support.js";
|
|
4
5
|
const OPTION_KEYS = ["versions", "os"];
|
|
5
6
|
/** The project's own <version>: parent, dependency and plugin versions are removed first. */
|
|
@@ -22,11 +23,7 @@ export const javaStack = {
|
|
|
22
23
|
release = { type: "maven", version: mavenVersion((await readText(root, "pom.xml")) ?? "") };
|
|
23
24
|
}
|
|
24
25
|
else {
|
|
25
|
-
|
|
26
|
-
const properties = await readText(root, "gradle.properties");
|
|
27
|
-
const version = properties ? (/^version\s*=\s*(\S+)/m.exec(properties)?.[1] ?? null) : null;
|
|
28
|
-
// release-please's generic updater changes gradle.properties once it carries x-release-please markers
|
|
29
|
-
release = { type: "simple", version, ...(properties !== null ? { extraFiles: ["gradle.properties"] } : {}) };
|
|
26
|
+
({ command, release } = await gradleBuild(root));
|
|
30
27
|
}
|
|
31
28
|
return {
|
|
32
29
|
id: "java",
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { UsageError } from "../errors.js";
|
|
4
|
+
import { GRADLE_FILES, gradleBuild } from "./gradle.js";
|
|
5
|
+
import { checkKeys, stringList } from "./support.js";
|
|
6
|
+
const OPTION_KEYS = ["versions", "os"];
|
|
7
|
+
// plugin ids, the kotlin() shorthand and version-catalog aliases; org.jetbrains.kotlinx is a library, not the plugin
|
|
8
|
+
const KOTLIN = /\bkotlin\(\s*"|org\.jetbrains\.kotlin[."']|libs\.plugins\.kotlin\b/;
|
|
9
|
+
const ANDROID = /com\.android\.(application|library)|libs\.plugins\.android\b/;
|
|
10
|
+
/** The root Gradle build files, with `//` comments removed. */
|
|
11
|
+
function buildFiles(root) {
|
|
12
|
+
return GRADLE_FILES.flatMap((file) => {
|
|
13
|
+
try {
|
|
14
|
+
return [{ file, text: readFileSync(join(root, file), "utf8").replace(/\/\/.*$/gm, "") }];
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/** Whether a Gradle build at the root applies a Kotlin plugin. */
|
|
22
|
+
export const usesKotlin = (root) => buildFiles(root).some(({ text }) => KOTLIN.test(text));
|
|
23
|
+
export const kotlinStack = {
|
|
24
|
+
id: "kotlin",
|
|
25
|
+
detect: usesKotlin,
|
|
26
|
+
async resolve(root, options = {}) {
|
|
27
|
+
checkKeys("kotlin", options, OPTION_KEYS);
|
|
28
|
+
const android = buildFiles(root).find(({ text }) => ANDROID.test(text));
|
|
29
|
+
if (android) {
|
|
30
|
+
throw new UsageError(`${android.file} is an Android project, which needs the Android SDK the CI runners don't have; leave kotlin out of stacks`);
|
|
31
|
+
}
|
|
32
|
+
const { command, release, wrapper } = await gradleBuild(root);
|
|
33
|
+
return {
|
|
34
|
+
id: "kotlin",
|
|
35
|
+
// ktlint or Spotless run through the build, like Spotless for java
|
|
36
|
+
staged: [],
|
|
37
|
+
test: command,
|
|
38
|
+
install: null,
|
|
39
|
+
gitignore: ["Kotlin", "Gradle"],
|
|
40
|
+
dependabot: ["gradle"],
|
|
41
|
+
ci: {
|
|
42
|
+
workflow: "stack-java.yml",
|
|
43
|
+
with: {
|
|
44
|
+
"java-versions": JSON.stringify(stringList("kotlin", options, "versions") ?? ["17", "21"]),
|
|
45
|
+
os: JSON.stringify(stringList("kotlin", options, "os") ?? ["ubuntu-latest"]),
|
|
46
|
+
"build-tool": "gradle",
|
|
47
|
+
"gradle-version": wrapper ? "" : "current",
|
|
48
|
+
commands: JSON.stringify([command]),
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
release,
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { UsageError } from "../errors.js";
|
|
4
|
+
import { checkKeys, readText, stringList } from "./support.js";
|
|
5
|
+
const OPTION_KEYS = ["versions", "os"];
|
|
6
|
+
export const phpStack = {
|
|
7
|
+
id: "php",
|
|
8
|
+
detect: (root) => existsSync(join(root, "composer.json")),
|
|
9
|
+
async resolve(root, options = {}) {
|
|
10
|
+
checkKeys("php", options, OPTION_KEYS);
|
|
11
|
+
const has = (path) => existsSync(join(root, path));
|
|
12
|
+
let composer;
|
|
13
|
+
try {
|
|
14
|
+
composer = JSON.parse((await readText(root, "composer.json")) ?? "{}");
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
throw new UsageError(`composer.json could not be read: ${error.message}`);
|
|
18
|
+
}
|
|
19
|
+
const csFixer = has(".php-cs-fixer.dist.php") || has(".php-cs-fixer.php");
|
|
20
|
+
const phpstan = has("phpstan.neon") || has("phpstan.neon.dist") || has("phpstan.dist.neon");
|
|
21
|
+
const test = composer.scripts?.test
|
|
22
|
+
? "composer test"
|
|
23
|
+
: has("phpunit.xml") || has("phpunit.xml.dist") || has("phpunit.dist.xml")
|
|
24
|
+
? "vendor/bin/phpunit"
|
|
25
|
+
: null;
|
|
26
|
+
return {
|
|
27
|
+
id: "php",
|
|
28
|
+
staged: csFixer
|
|
29
|
+
? [{ name: "php:cs-fixer", glob: "*.php", run: "vendor/bin/php-cs-fixer fix {staged_files}" }]
|
|
30
|
+
: [],
|
|
31
|
+
test,
|
|
32
|
+
install: "composer install",
|
|
33
|
+
gitignore: ["Composer"],
|
|
34
|
+
dependabot: ["composer"],
|
|
35
|
+
ci: {
|
|
36
|
+
workflow: "stack-php.yml",
|
|
37
|
+
with: {
|
|
38
|
+
"php-versions": JSON.stringify(stringList("php", options, "versions") ?? ["8.3", "8.4"]),
|
|
39
|
+
os: JSON.stringify(stringList("php", options, "os") ?? ["ubuntu-latest"]),
|
|
40
|
+
"install-command": "composer install --no-interaction --no-progress",
|
|
41
|
+
commands: JSON.stringify([
|
|
42
|
+
// --strict would fail on warnings such as a missing license or the version field release-please writes
|
|
43
|
+
"composer validate --no-check-publish",
|
|
44
|
+
...(csFixer ? ["vendor/bin/php-cs-fixer fix --dry-run --diff"] : []),
|
|
45
|
+
...(phpstan ? ["vendor/bin/phpstan analyse --no-progress"] : []),
|
|
46
|
+
...(test ? [test] : []),
|
|
47
|
+
]),
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
release: { type: "php", version: composer.version ?? null },
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
package/dist/stacks/python.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { checkKeys, filesMatching, optionalString, readText, stringList } from "./support.js";
|
|
3
|
+
import { checkKeys, filesMatching, optionalString, readText, stringList, tomlSection } from "./support.js";
|
|
4
4
|
const OPTION_KEYS = ["versions", "os", "test"];
|
|
5
5
|
/** The `version = "…"` of the `[project]` table, if any. */
|
|
6
6
|
function projectVersion(pyproject) {
|
|
7
|
-
return /^version\s*=\s*"([^"]+)"/m.exec(
|
|
8
|
-
}
|
|
9
|
-
/** The body of the `[name]` table of a TOML file, or "". */
|
|
10
|
-
function section(toml, name) {
|
|
11
|
-
return toml.split(/^\[/m).find((part) => part.startsWith(`${name}]`)) ?? "";
|
|
7
|
+
return /^version\s*=\s*"([^"]+)"/m.exec(tomlSection(pyproject, "project"))?.[1] ?? null;
|
|
12
8
|
}
|
|
13
9
|
export const pythonStack = {
|
|
14
10
|
id: "python",
|
|
@@ -21,7 +17,7 @@ export const pythonStack = {
|
|
|
21
17
|
const mypyIni = (await readText(root, "mypy.ini")) ?? "";
|
|
22
18
|
const mypy = has("mypy.ini") || pyproject.includes("[tool.mypy]");
|
|
23
19
|
// a config that names its files decides what mypy checks; otherwise check the whole tree
|
|
24
|
-
const mypyFiles = /^files\s*=/m.test(mypyIni) || /^files\s*=/m.test(
|
|
20
|
+
const mypyFiles = /^files\s*=/m.test(mypyIni) || /^files\s*=/m.test(tomlSection(pyproject, "tool.mypy"));
|
|
25
21
|
const hasTests = has("tests") || has("test") || filesMatching(root, ".", /^test_.*\.py$/).length > 0;
|
|
26
22
|
const customTest = optionalString("python", options, "test");
|
|
27
23
|
const pytest = customTest === undefined && hasTests;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { checkKeys, filesMatching, readText, stringList } from "./support.js";
|
|
4
|
+
const OPTION_KEYS = ["versions", "os"];
|
|
5
|
+
// only the gem's own version: not MINIMUM_RUBY_VERSION, required_ruby_version or a computed value
|
|
6
|
+
const VERSION_CONSTANT = /^\s*VERSION\s*=\s*["']([^"']+)["']/m;
|
|
7
|
+
const GEMSPEC_VERSION = /\.version\s*=\s*["']([^"']+)["']/;
|
|
8
|
+
/** A `VERSION = "x.y.z"` in lib/<name>/version.rb, else the gemspec's `s.version = "x.y.z"`. */
|
|
9
|
+
async function rubyRelease(root) {
|
|
10
|
+
for (const dir of filesMatching(root, "lib", /^[^.]+$/)) {
|
|
11
|
+
const path = `lib/${dir}/version.rb`;
|
|
12
|
+
const version = VERSION_CONSTANT.exec((await readText(root, path)) ?? "")?.[1];
|
|
13
|
+
if (version)
|
|
14
|
+
return { type: "ruby", version, versionFile: path };
|
|
15
|
+
}
|
|
16
|
+
for (const spec of filesMatching(root, ".", /\.gemspec$/)) {
|
|
17
|
+
const version = GEMSPEC_VERSION.exec((await readText(root, spec)) ?? "")?.[1];
|
|
18
|
+
// release-please's ruby strategy only bumps version.rb; the generic updater bumps the gemspec
|
|
19
|
+
if (version)
|
|
20
|
+
return { type: "ruby", version, extraFiles: [spec] };
|
|
21
|
+
}
|
|
22
|
+
return { type: "ruby", version: null };
|
|
23
|
+
}
|
|
24
|
+
export const rubyStack = {
|
|
25
|
+
id: "ruby",
|
|
26
|
+
detect: (root) => existsSync(join(root, "Gemfile")),
|
|
27
|
+
async resolve(root, options = {}) {
|
|
28
|
+
checkKeys("ruby", options, OPTION_KEYS);
|
|
29
|
+
const has = (path) => existsSync(join(root, path));
|
|
30
|
+
const rubocop = has(".rubocop.yml");
|
|
31
|
+
const test = has("spec") ? "bundle exec rspec" : has("Rakefile") && has("test") ? "bundle exec rake test" : null;
|
|
32
|
+
return {
|
|
33
|
+
id: "ruby",
|
|
34
|
+
staged: rubocop
|
|
35
|
+
? [{ name: "ruby:rubocop", glob: "*.rb", run: "bundle exec rubocop --autocorrect {staged_files}" }]
|
|
36
|
+
: [],
|
|
37
|
+
test,
|
|
38
|
+
install: "bundle install",
|
|
39
|
+
gitignore: ["Ruby"],
|
|
40
|
+
dependabot: ["bundler"],
|
|
41
|
+
ci: {
|
|
42
|
+
workflow: "stack-ruby.yml",
|
|
43
|
+
with: {
|
|
44
|
+
"ruby-versions": JSON.stringify(stringList("ruby", options, "versions") ?? ["3.3", "3.4"]),
|
|
45
|
+
os: JSON.stringify(stringList("ruby", options, "os") ?? ["ubuntu-latest"]),
|
|
46
|
+
"install-command": "bundle install",
|
|
47
|
+
commands: JSON.stringify([...(rubocop ? ["bundle exec rubocop"] : []), ...(test ? [test] : [])]),
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
release: await rubyRelease(root),
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { checkKeys, readText, stringList, tomlSection } from "./support.js";
|
|
4
|
+
const OPTION_KEYS = ["versions", "os"];
|
|
5
|
+
export const rustStack = {
|
|
6
|
+
id: "rust",
|
|
7
|
+
detect: (root) => existsSync(join(root, "Cargo.toml")),
|
|
8
|
+
async resolve(root, options = {}) {
|
|
9
|
+
checkKeys("rust", options, OPTION_KEYS);
|
|
10
|
+
const cargo = (await readText(root, "Cargo.toml")) ?? "";
|
|
11
|
+
const pkg = tomlSection(cargo, "package");
|
|
12
|
+
const version = /^version\s*=\s*"([^"]+)"/m.exec(pkg)?.[1] ?? null;
|
|
13
|
+
// a workspace root without its own package has nothing for release-please's rust updater to bump
|
|
14
|
+
const release = pkg ? { type: "rust", version } : { type: "simple", version: null };
|
|
15
|
+
return {
|
|
16
|
+
id: "rust",
|
|
17
|
+
staged: [{ name: "rust:fmt", glob: "*.rs", run: "cargo fmt --all" }],
|
|
18
|
+
test: "cargo test",
|
|
19
|
+
install: "cargo fetch",
|
|
20
|
+
gitignore: ["Rust"],
|
|
21
|
+
dependabot: ["cargo"],
|
|
22
|
+
ci: {
|
|
23
|
+
workflow: "stack-rust.yml",
|
|
24
|
+
with: {
|
|
25
|
+
"rust-versions": JSON.stringify(stringList("rust", options, "versions") ?? ["stable"]),
|
|
26
|
+
os: JSON.stringify(stringList("rust", options, "os") ?? ["ubuntu-latest"]),
|
|
27
|
+
commands: JSON.stringify([
|
|
28
|
+
"cargo fmt --all --check",
|
|
29
|
+
"cargo clippy --workspace --all-targets -- -D warnings",
|
|
30
|
+
"cargo test --workspace --all-targets",
|
|
31
|
+
// --all-targets leaves doctests out, and --doc fails when there is no library to document
|
|
32
|
+
...(existsSync(join(root, "src/lib.rs")) ? ["cargo test --workspace --doc"] : []),
|
|
33
|
+
]),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
release,
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
};
|
package/dist/stacks/support.js
CHANGED
package/dist/version.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from "node:fs";
|
|
2
2
|
export const PACKAGE_VERSION = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version;
|
|
3
3
|
/** The standard this build of repokeeper applies. Bump it whenever generated output changes. */
|
|
4
|
-
export const STANDARD_VERSION = "1.
|
|
4
|
+
export const STANDARD_VERSION = "1.3.1";
|
|
5
5
|
export const TOOL_VERSIONS = {
|
|
6
6
|
lefthook: "2.1.14",
|
|
7
7
|
commitlintCli: "21.2.3",
|
package/package.json
CHANGED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
composer.phar
|
|
2
|
+
/vendor/
|
|
3
|
+
|
|
4
|
+
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
|
|
5
|
+
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
|
|
6
|
+
# composer.lock
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# If you prefer the allow list template instead of the deny list, see community template:
|
|
2
|
+
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
|
3
|
+
#
|
|
4
|
+
# Binaries for programs and plugins
|
|
5
|
+
*.exe
|
|
6
|
+
*.exe~
|
|
7
|
+
*.dll
|
|
8
|
+
*.so
|
|
9
|
+
*.dylib
|
|
10
|
+
|
|
11
|
+
# Test binary, built with `go test -c`
|
|
12
|
+
*.test
|
|
13
|
+
|
|
14
|
+
# Code coverage profiles and other test artifacts
|
|
15
|
+
*.out
|
|
16
|
+
coverage.*
|
|
17
|
+
*.coverprofile
|
|
18
|
+
profile.cov
|
|
19
|
+
|
|
20
|
+
# Dependency directories (remove the comment below to include it)
|
|
21
|
+
# vendor/
|
|
22
|
+
|
|
23
|
+
# Go workspace file
|
|
24
|
+
go.work
|
|
25
|
+
go.work.sum
|
|
26
|
+
|
|
27
|
+
# env file
|
|
28
|
+
.env
|
|
29
|
+
|
|
30
|
+
# Editor/IDE
|
|
31
|
+
# .idea/
|
|
32
|
+
# .vscode/
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Compiled class file
|
|
2
|
+
*.class
|
|
3
|
+
|
|
4
|
+
# Log file
|
|
5
|
+
*.log
|
|
6
|
+
|
|
7
|
+
# BlueJ files
|
|
8
|
+
*.ctxt
|
|
9
|
+
|
|
10
|
+
# Mobile Tools for Java (J2ME)
|
|
11
|
+
.mtj.tmp/
|
|
12
|
+
|
|
13
|
+
# Package Files #
|
|
14
|
+
*.jar
|
|
15
|
+
*.war
|
|
16
|
+
*.nar
|
|
17
|
+
*.ear
|
|
18
|
+
*.zip
|
|
19
|
+
*.tar.gz
|
|
20
|
+
*.rar
|
|
21
|
+
|
|
22
|
+
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
|
23
|
+
hs_err_pid*
|
|
24
|
+
replay_pid*
|
|
25
|
+
|
|
26
|
+
# Kotlin Gradle plugin data, see https://kotlinlang.org/docs/whatsnew20.html#new-directory-for-kotlin-data-in-gradle-projects
|
|
27
|
+
.kotlin/
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
/.config
|
|
4
|
+
/coverage/
|
|
5
|
+
/InstalledFiles
|
|
6
|
+
/pkg/
|
|
7
|
+
/spec/reports/
|
|
8
|
+
/spec/examples.txt
|
|
9
|
+
/test/tmp/
|
|
10
|
+
/test/version_tmp/
|
|
11
|
+
/tmp/
|
|
12
|
+
|
|
13
|
+
# Used by dotenv library to load environment variables.
|
|
14
|
+
# .env
|
|
15
|
+
|
|
16
|
+
# Ignore Byebug command history file.
|
|
17
|
+
.byebug_history
|
|
18
|
+
|
|
19
|
+
## Specific to RubyMotion:
|
|
20
|
+
.dat*
|
|
21
|
+
.repl_history
|
|
22
|
+
build/
|
|
23
|
+
*.bridgesupport
|
|
24
|
+
build-iPhoneOS/
|
|
25
|
+
build-iPhoneSimulator/
|
|
26
|
+
|
|
27
|
+
## Specific to RubyMotion (use of CocoaPods):
|
|
28
|
+
#
|
|
29
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
|
30
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
|
31
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
|
32
|
+
#
|
|
33
|
+
# vendor/Pods/
|
|
34
|
+
|
|
35
|
+
## Documentation cache and generated files:
|
|
36
|
+
/.yardoc/
|
|
37
|
+
/_yardoc/
|
|
38
|
+
/doc/
|
|
39
|
+
/rdoc/
|
|
40
|
+
|
|
41
|
+
## Environment normalization:
|
|
42
|
+
/.bundle/
|
|
43
|
+
/vendor/bundle
|
|
44
|
+
/lib/bundler/man/
|
|
45
|
+
|
|
46
|
+
# for a library or gem, you might want to ignore these files since the code is
|
|
47
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
48
|
+
# Gemfile.lock
|
|
49
|
+
# .ruby-version
|
|
50
|
+
# .ruby-gemset
|
|
51
|
+
|
|
52
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
|
53
|
+
.rvmrc
|
|
54
|
+
|
|
55
|
+
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
|
56
|
+
# .rubocop-https?--*
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Generated by Cargo
|
|
2
|
+
# will have compiled files and executables
|
|
3
|
+
debug
|
|
4
|
+
target
|
|
5
|
+
|
|
6
|
+
# These are backup files generated by rustfmt
|
|
7
|
+
**/*.rs.bk
|
|
8
|
+
|
|
9
|
+
# MSVC Windows builds of rustc generate these, which store debugging information
|
|
10
|
+
*.pdb
|
|
11
|
+
|
|
12
|
+
# Generated by cargo mutants
|
|
13
|
+
# Contains mutation testing data
|
|
14
|
+
**/mutants.out*/
|
|
15
|
+
|
|
16
|
+
# rustc will dump stack traces when hitting an internal compiler error to PWD
|
|
17
|
+
rustc-ice-*.txt
|
|
18
|
+
|
|
19
|
+
# RustRover
|
|
20
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
21
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
22
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
23
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
24
|
+
#.idea/
|