@rindrics/initrepo 0.2.1 → 0.3.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/.github/workflows/tagpr.yml +3 -1
- package/CHANGELOG.md +7 -0
- package/dist/cli.js +187 -1
- package/package.json +1 -1
- package/renovate.json5 +14 -0
- package/src/cli.ts +4 -0
- package/src/commands/setup-renovate.ts +138 -0
- package/src/commands/setup-tagpr.ts +147 -0
- package/src/templates/common/renovate.json5.ejs +14 -0
- package/.github/dependabot.yml +0 -11
|
@@ -16,11 +16,13 @@ jobs:
|
|
|
16
16
|
with:
|
|
17
17
|
token: ${{ secrets.PAT_FOR_TAGPR }}
|
|
18
18
|
|
|
19
|
-
- name: Configure SSH signing
|
|
19
|
+
- name: Configure Git and SSH signing
|
|
20
20
|
run: |
|
|
21
21
|
mkdir -p ~/.ssh
|
|
22
22
|
echo "${{ secrets.SSH_SIGNING_KEY }}" > ~/.ssh/signing_key
|
|
23
23
|
chmod 600 ~/.ssh/signing_key
|
|
24
|
+
git config --global user.name "Rindrics"
|
|
25
|
+
git config --global user.email "${{ secrets.GIT_USER_EMAIL }}"
|
|
24
26
|
git config --global gpg.format ssh
|
|
25
27
|
git config --global user.signingkey ~/.ssh/signing_key
|
|
26
28
|
git config --global commit.gpgsign true
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [v0.3.1](https://github.com/Rindrics/initrepo/compare/v0.3.0...v0.3.1) - 2026-01-19
|
|
4
|
+
- feat: add renovate command by @Rindrics in https://github.com/Rindrics/initrepo/pull/38
|
|
5
|
+
|
|
6
|
+
## [v0.3.0](https://github.com/Rindrics/initrepo/compare/v0.2.1...v0.3.0) - 2026-01-19
|
|
7
|
+
- ci: use renovate to use bun by @Rindrics in https://github.com/Rindrics/initrepo/pull/35
|
|
8
|
+
- feat: generate tagpr config by @Rindrics in https://github.com/Rindrics/initrepo/pull/37
|
|
9
|
+
|
|
3
10
|
## [v0.2.1](https://github.com/Rindrics/initrepo/compare/v0.2.0...v0.2.1) - 2026-01-19
|
|
4
11
|
- docs: update README.md by @Rindrics in https://github.com/Rindrics/initrepo/pull/24
|
|
5
12
|
- feat: add command setup-husky by @Rindrics in https://github.com/Rindrics/initrepo/pull/32
|
package/dist/cli.js
CHANGED
|
@@ -4207,7 +4207,7 @@ var {
|
|
|
4207
4207
|
// package.json
|
|
4208
4208
|
var package_default = {
|
|
4209
4209
|
name: "@rindrics/initrepo",
|
|
4210
|
-
version: "0.
|
|
4210
|
+
version: "0.3.1",
|
|
4211
4211
|
description: "setup GitHub repo with dev tools",
|
|
4212
4212
|
type: "module",
|
|
4213
4213
|
bin: {
|
|
@@ -4363,6 +4363,21 @@ bun run build
|
|
|
4363
4363
|
exclude:
|
|
4364
4364
|
labels:
|
|
4365
4365
|
- tagpr
|
|
4366
|
+
`,
|
|
4367
|
+
"common/renovate.json5.ejs": `{
|
|
4368
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
4369
|
+
"extends": ["config:recommended"],
|
|
4370
|
+
"packageRules": [
|
|
4371
|
+
{
|
|
4372
|
+
"matchUpdateTypes": ["major", "minor", "patch"],
|
|
4373
|
+
"automerge": true
|
|
4374
|
+
}
|
|
4375
|
+
],
|
|
4376
|
+
"lockFileMaintenance": {
|
|
4377
|
+
"enabled": true,
|
|
4378
|
+
"schedule": ["before 5am on monday"]
|
|
4379
|
+
}
|
|
4380
|
+
}
|
|
4366
4381
|
`,
|
|
4367
4382
|
"common/workflows/publish.yml.ejs": `name: Publish to npm
|
|
4368
4383
|
|
|
@@ -11641,6 +11656,175 @@ function registerSetupHuskyCommand(program2) {
|
|
|
11641
11656
|
});
|
|
11642
11657
|
}
|
|
11643
11658
|
|
|
11659
|
+
// src/commands/setup-renovate.ts
|
|
11660
|
+
import * as fs4 from "node:fs/promises";
|
|
11661
|
+
import * as path4 from "node:path";
|
|
11662
|
+
async function fileExists2(filePath) {
|
|
11663
|
+
try {
|
|
11664
|
+
await fs4.access(filePath);
|
|
11665
|
+
return true;
|
|
11666
|
+
} catch {
|
|
11667
|
+
return false;
|
|
11668
|
+
}
|
|
11669
|
+
}
|
|
11670
|
+
function generateRenovateSetup() {
|
|
11671
|
+
return [
|
|
11672
|
+
{
|
|
11673
|
+
path: "renovate.json5",
|
|
11674
|
+
content: loadTemplate("common/renovate.json5.ejs", {})
|
|
11675
|
+
}
|
|
11676
|
+
];
|
|
11677
|
+
}
|
|
11678
|
+
async function writeFiles2(targetDir, files, force) {
|
|
11679
|
+
const written = [];
|
|
11680
|
+
const skipped = [];
|
|
11681
|
+
for (const file of files) {
|
|
11682
|
+
const filePath = path4.join(targetDir, file.path);
|
|
11683
|
+
const fileDir = path4.dirname(filePath);
|
|
11684
|
+
if (!force && await fileExists2(filePath)) {
|
|
11685
|
+
skipped.push(file.path);
|
|
11686
|
+
continue;
|
|
11687
|
+
}
|
|
11688
|
+
await fs4.mkdir(fileDir, { recursive: true });
|
|
11689
|
+
await fs4.writeFile(filePath, file.content, "utf-8");
|
|
11690
|
+
written.push(file.path);
|
|
11691
|
+
}
|
|
11692
|
+
return { written, skipped };
|
|
11693
|
+
}
|
|
11694
|
+
async function setupRenovate(options) {
|
|
11695
|
+
const targetDir = options.targetDir ?? process.cwd();
|
|
11696
|
+
const force = options.force ?? false;
|
|
11697
|
+
const packageJsonPath = path4.join(targetDir, "package.json");
|
|
11698
|
+
if (!await fileExists2(packageJsonPath)) {
|
|
11699
|
+
throw new Error("package.json not found. Are you in a project directory?");
|
|
11700
|
+
}
|
|
11701
|
+
console.log(`\uD83D\uDD27 Setting up Renovate configuration...
|
|
11702
|
+
`);
|
|
11703
|
+
const files = generateRenovateSetup();
|
|
11704
|
+
const { written, skipped } = await writeFiles2(targetDir, files, force);
|
|
11705
|
+
if (written.length > 0) {
|
|
11706
|
+
console.log("✅ Created files:");
|
|
11707
|
+
for (const file of written) {
|
|
11708
|
+
console.log(` ${file}`);
|
|
11709
|
+
}
|
|
11710
|
+
}
|
|
11711
|
+
if (skipped.length > 0) {
|
|
11712
|
+
console.log(`
|
|
11713
|
+
⏭️ Skipped (already exist, use --force to overwrite):`);
|
|
11714
|
+
for (const file of skipped) {
|
|
11715
|
+
console.log(` ${file}`);
|
|
11716
|
+
}
|
|
11717
|
+
}
|
|
11718
|
+
console.log(`
|
|
11719
|
+
\uD83C\uDF89 Renovate setup complete!`);
|
|
11720
|
+
console.log(`
|
|
11721
|
+
\uD83D\uDCCB Next steps:`);
|
|
11722
|
+
console.log(" 1. Install Renovate GitHub App: https://github.com/apps/renovate");
|
|
11723
|
+
console.log(" 2. Grant access to this repository");
|
|
11724
|
+
}
|
|
11725
|
+
function registerSetupRenovateCommand(program2) {
|
|
11726
|
+
program2.command("setup-renovate").description("Set up Renovate configuration in an existing project").option("-t, --target-dir <path>", "Target directory (defaults to current directory)").option("-f, --force", "Overwrite existing files").action(async (opts) => {
|
|
11727
|
+
try {
|
|
11728
|
+
await setupRenovate({
|
|
11729
|
+
targetDir: opts.targetDir,
|
|
11730
|
+
force: opts.force
|
|
11731
|
+
});
|
|
11732
|
+
} catch (error) {
|
|
11733
|
+
console.error(`❌ Failed to setup Renovate: ${error instanceof Error ? error.message : String(error)}`);
|
|
11734
|
+
process.exit(1);
|
|
11735
|
+
}
|
|
11736
|
+
});
|
|
11737
|
+
}
|
|
11738
|
+
|
|
11739
|
+
// src/commands/setup-tagpr.ts
|
|
11740
|
+
import * as fs5 from "node:fs/promises";
|
|
11741
|
+
import * as path5 from "node:path";
|
|
11742
|
+
async function fileExists3(filePath) {
|
|
11743
|
+
try {
|
|
11744
|
+
await fs5.access(filePath);
|
|
11745
|
+
return true;
|
|
11746
|
+
} catch {
|
|
11747
|
+
return false;
|
|
11748
|
+
}
|
|
11749
|
+
}
|
|
11750
|
+
async function generateTagprSetup() {
|
|
11751
|
+
const actionVersions = await getLatestActionVersions();
|
|
11752
|
+
return [
|
|
11753
|
+
{
|
|
11754
|
+
path: ".tagpr",
|
|
11755
|
+
content: loadTemplate("typescript/.tagpr.ejs", {})
|
|
11756
|
+
},
|
|
11757
|
+
{
|
|
11758
|
+
path: ".github/workflows/tagpr.yml",
|
|
11759
|
+
content: loadTemplate("common/workflows/tagpr.yml.ejs", {
|
|
11760
|
+
isDevcode: false,
|
|
11761
|
+
actionVersions
|
|
11762
|
+
})
|
|
11763
|
+
}
|
|
11764
|
+
];
|
|
11765
|
+
}
|
|
11766
|
+
async function writeFiles3(targetDir, files, force) {
|
|
11767
|
+
const written = [];
|
|
11768
|
+
const skipped = [];
|
|
11769
|
+
for (const file of files) {
|
|
11770
|
+
const filePath = path5.join(targetDir, file.path);
|
|
11771
|
+
const fileDir = path5.dirname(filePath);
|
|
11772
|
+
if (!force && await fileExists3(filePath)) {
|
|
11773
|
+
skipped.push(file.path);
|
|
11774
|
+
continue;
|
|
11775
|
+
}
|
|
11776
|
+
await fs5.mkdir(fileDir, { recursive: true });
|
|
11777
|
+
await fs5.writeFile(filePath, file.content, "utf-8");
|
|
11778
|
+
written.push(file.path);
|
|
11779
|
+
}
|
|
11780
|
+
return { written, skipped };
|
|
11781
|
+
}
|
|
11782
|
+
async function setupTagpr(options) {
|
|
11783
|
+
const targetDir = options.targetDir ?? process.cwd();
|
|
11784
|
+
const force = options.force ?? false;
|
|
11785
|
+
const packageJsonPath = path5.join(targetDir, "package.json");
|
|
11786
|
+
if (!await fileExists3(packageJsonPath)) {
|
|
11787
|
+
throw new Error("package.json not found. Are you in a project directory?");
|
|
11788
|
+
}
|
|
11789
|
+
console.log(`\uD83D\uDD27 Setting up tagpr configuration...
|
|
11790
|
+
`);
|
|
11791
|
+
const files = await generateTagprSetup();
|
|
11792
|
+
const { written, skipped } = await writeFiles3(targetDir, files, force);
|
|
11793
|
+
if (written.length > 0) {
|
|
11794
|
+
console.log("✅ Created files:");
|
|
11795
|
+
for (const file of written) {
|
|
11796
|
+
console.log(` ${file}`);
|
|
11797
|
+
}
|
|
11798
|
+
}
|
|
11799
|
+
if (skipped.length > 0) {
|
|
11800
|
+
console.log(`
|
|
11801
|
+
⏭️ Skipped (already exist, use --force to overwrite):`);
|
|
11802
|
+
for (const file of skipped) {
|
|
11803
|
+
console.log(` ${file}`);
|
|
11804
|
+
}
|
|
11805
|
+
}
|
|
11806
|
+
console.log(`
|
|
11807
|
+
\uD83C\uDF89 Tagpr setup complete!`);
|
|
11808
|
+
console.log(`
|
|
11809
|
+
\uD83D\uDCCB Required secrets:`);
|
|
11810
|
+
console.log(" PAT_FOR_TAGPR - Personal Access Token with repo & workflow permissions");
|
|
11811
|
+
console.log(" SSH_SIGNING_KEY - SSH private key for commit signing");
|
|
11812
|
+
console.log(" GIT_USER_EMAIL - Email address for commits (optional)");
|
|
11813
|
+
}
|
|
11814
|
+
function registerSetupTagprCommand(program2) {
|
|
11815
|
+
program2.command("setup-tagpr").description("Set up tagpr configuration in an existing project").option("-t, --target-dir <path>", "Target directory (defaults to current directory)").option("-f, --force", "Overwrite existing files").action(async (opts) => {
|
|
11816
|
+
try {
|
|
11817
|
+
await setupTagpr({
|
|
11818
|
+
targetDir: opts.targetDir,
|
|
11819
|
+
force: opts.force
|
|
11820
|
+
});
|
|
11821
|
+
} catch (error) {
|
|
11822
|
+
console.error(`❌ Failed to setup tagpr: ${error instanceof Error ? error.message : String(error)}`);
|
|
11823
|
+
process.exit(1);
|
|
11824
|
+
}
|
|
11825
|
+
});
|
|
11826
|
+
}
|
|
11827
|
+
|
|
11644
11828
|
// src/cli.ts
|
|
11645
11829
|
var { version: VERSION17, name: NAME } = package_default;
|
|
11646
11830
|
function createProgram() {
|
|
@@ -11649,6 +11833,8 @@ function createProgram() {
|
|
|
11649
11833
|
registerInitCommand(program2);
|
|
11650
11834
|
registerPrepareReleaseCommand(program2);
|
|
11651
11835
|
registerSetupHuskyCommand(program2);
|
|
11836
|
+
registerSetupRenovateCommand(program2);
|
|
11837
|
+
registerSetupTagprCommand(program2);
|
|
11652
11838
|
return program2;
|
|
11653
11839
|
}
|
|
11654
11840
|
if (__require.main == __require.module) {
|
package/package.json
CHANGED
package/renovate.json5
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
+
"extends": ["config:recommended"],
|
|
4
|
+
"packageRules": [
|
|
5
|
+
{
|
|
6
|
+
"matchUpdateTypes": ["major", "minor", "patch"],
|
|
7
|
+
"automerge": true
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"lockFileMaintenance": {
|
|
11
|
+
"enabled": true,
|
|
12
|
+
"schedule": ["before 5am on monday"]
|
|
13
|
+
}
|
|
14
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -4,6 +4,8 @@ import packageJson from '../package.json';
|
|
|
4
4
|
import { registerInitCommand } from './commands/init';
|
|
5
5
|
import { registerPrepareReleaseCommand } from './commands/prepare-release';
|
|
6
6
|
import { registerSetupHuskyCommand } from './commands/setup-husky';
|
|
7
|
+
import { registerSetupRenovateCommand } from './commands/setup-renovate';
|
|
8
|
+
import { registerSetupTagprCommand } from './commands/setup-tagpr';
|
|
7
9
|
|
|
8
10
|
const { version: VERSION, name: NAME } = packageJson;
|
|
9
11
|
|
|
@@ -18,6 +20,8 @@ export function createProgram(): Command {
|
|
|
18
20
|
registerInitCommand(program);
|
|
19
21
|
registerPrepareReleaseCommand(program);
|
|
20
22
|
registerSetupHuskyCommand(program);
|
|
23
|
+
registerSetupRenovateCommand(program);
|
|
24
|
+
registerSetupTagprCommand(program);
|
|
21
25
|
|
|
22
26
|
return program;
|
|
23
27
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import type { Command } from 'commander';
|
|
4
|
+
import type { GeneratedFile } from '../generators/project';
|
|
5
|
+
import { loadTemplate } from '../generators/project';
|
|
6
|
+
|
|
7
|
+
export interface SetupRenovateOptions {
|
|
8
|
+
/** Target directory (defaults to current directory) */
|
|
9
|
+
targetDir?: string;
|
|
10
|
+
/** Force overwrite existing files */
|
|
11
|
+
force?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Check if file exists
|
|
16
|
+
*/
|
|
17
|
+
async function fileExists(filePath: string): Promise<boolean> {
|
|
18
|
+
try {
|
|
19
|
+
await fs.access(filePath);
|
|
20
|
+
return true;
|
|
21
|
+
} catch {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Generate renovate configuration
|
|
28
|
+
*/
|
|
29
|
+
function generateRenovateSetup(): GeneratedFile[] {
|
|
30
|
+
return [
|
|
31
|
+
{
|
|
32
|
+
path: 'renovate.json5',
|
|
33
|
+
content: loadTemplate('common/renovate.json5.ejs', {}),
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Write generated files to target directory
|
|
40
|
+
*/
|
|
41
|
+
async function writeFiles(
|
|
42
|
+
targetDir: string,
|
|
43
|
+
files: GeneratedFile[],
|
|
44
|
+
force: boolean,
|
|
45
|
+
): Promise<{ written: string[]; skipped: string[] }> {
|
|
46
|
+
const written: string[] = [];
|
|
47
|
+
const skipped: string[] = [];
|
|
48
|
+
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
const filePath = path.join(targetDir, file.path);
|
|
51
|
+
const fileDir = path.dirname(filePath);
|
|
52
|
+
|
|
53
|
+
// Check if file exists
|
|
54
|
+
if (!force && (await fileExists(filePath))) {
|
|
55
|
+
skipped.push(file.path);
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Create directory if needed
|
|
60
|
+
await fs.mkdir(fileDir, { recursive: true });
|
|
61
|
+
|
|
62
|
+
// Write file
|
|
63
|
+
await fs.writeFile(filePath, file.content, 'utf-8');
|
|
64
|
+
written.push(file.path);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return { written, skipped };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Main setup-renovate logic
|
|
72
|
+
*/
|
|
73
|
+
export async function setupRenovate(
|
|
74
|
+
options: SetupRenovateOptions,
|
|
75
|
+
): Promise<void> {
|
|
76
|
+
const targetDir = options.targetDir ?? process.cwd();
|
|
77
|
+
const force = options.force ?? false;
|
|
78
|
+
|
|
79
|
+
// Check if package.json exists
|
|
80
|
+
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
81
|
+
if (!(await fileExists(packageJsonPath))) {
|
|
82
|
+
throw new Error('package.json not found. Are you in a project directory?');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log('🔧 Setting up Renovate configuration...\n');
|
|
86
|
+
|
|
87
|
+
// Generate renovate files
|
|
88
|
+
const files = generateRenovateSetup();
|
|
89
|
+
|
|
90
|
+
// Write files
|
|
91
|
+
const { written, skipped } = await writeFiles(targetDir, files, force);
|
|
92
|
+
|
|
93
|
+
// Report results
|
|
94
|
+
if (written.length > 0) {
|
|
95
|
+
console.log('✅ Created files:');
|
|
96
|
+
for (const file of written) {
|
|
97
|
+
console.log(` ${file}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (skipped.length > 0) {
|
|
102
|
+
console.log('\n⏭️ Skipped (already exist, use --force to overwrite):');
|
|
103
|
+
for (const file of skipped) {
|
|
104
|
+
console.log(` ${file}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log('\n🎉 Renovate setup complete!');
|
|
109
|
+
console.log('\n📋 Next steps:');
|
|
110
|
+
console.log(
|
|
111
|
+
' 1. Install Renovate GitHub App: https://github.com/apps/renovate',
|
|
112
|
+
);
|
|
113
|
+
console.log(' 2. Grant access to this repository');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function registerSetupRenovateCommand(program: Command): void {
|
|
117
|
+
program
|
|
118
|
+
.command('setup-renovate')
|
|
119
|
+
.description('Set up Renovate configuration in an existing project')
|
|
120
|
+
.option(
|
|
121
|
+
'-t, --target-dir <path>',
|
|
122
|
+
'Target directory (defaults to current directory)',
|
|
123
|
+
)
|
|
124
|
+
.option('-f, --force', 'Overwrite existing files')
|
|
125
|
+
.action(async (opts: { targetDir?: string; force?: boolean }) => {
|
|
126
|
+
try {
|
|
127
|
+
await setupRenovate({
|
|
128
|
+
targetDir: opts.targetDir,
|
|
129
|
+
force: opts.force,
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
console.error(
|
|
133
|
+
`❌ Failed to setup Renovate: ${error instanceof Error ? error.message : String(error)}`,
|
|
134
|
+
);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import * as fs from 'node:fs/promises';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import type { Command } from 'commander';
|
|
4
|
+
import type { GeneratedFile } from '../generators/project';
|
|
5
|
+
import { loadTemplate } from '../generators/project';
|
|
6
|
+
import { getLatestActionVersions } from '../utils/github';
|
|
7
|
+
|
|
8
|
+
export interface SetupTagprOptions {
|
|
9
|
+
/** Target directory (defaults to current directory) */
|
|
10
|
+
targetDir?: string;
|
|
11
|
+
/** Force overwrite existing files */
|
|
12
|
+
force?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Check if file exists
|
|
17
|
+
*/
|
|
18
|
+
async function fileExists(filePath: string): Promise<boolean> {
|
|
19
|
+
try {
|
|
20
|
+
await fs.access(filePath);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Generate tagpr-related files
|
|
29
|
+
*/
|
|
30
|
+
async function generateTagprSetup(): Promise<GeneratedFile[]> {
|
|
31
|
+
const actionVersions = await getLatestActionVersions();
|
|
32
|
+
|
|
33
|
+
return [
|
|
34
|
+
{
|
|
35
|
+
path: '.tagpr',
|
|
36
|
+
content: loadTemplate('typescript/.tagpr.ejs', {}),
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
path: '.github/workflows/tagpr.yml',
|
|
40
|
+
content: loadTemplate('common/workflows/tagpr.yml.ejs', {
|
|
41
|
+
isDevcode: false,
|
|
42
|
+
actionVersions,
|
|
43
|
+
}),
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Write generated files to target directory
|
|
50
|
+
*/
|
|
51
|
+
async function writeFiles(
|
|
52
|
+
targetDir: string,
|
|
53
|
+
files: GeneratedFile[],
|
|
54
|
+
force: boolean,
|
|
55
|
+
): Promise<{ written: string[]; skipped: string[] }> {
|
|
56
|
+
const written: string[] = [];
|
|
57
|
+
const skipped: string[] = [];
|
|
58
|
+
|
|
59
|
+
for (const file of files) {
|
|
60
|
+
const filePath = path.join(targetDir, file.path);
|
|
61
|
+
const fileDir = path.dirname(filePath);
|
|
62
|
+
|
|
63
|
+
// Check if file exists
|
|
64
|
+
if (!force && (await fileExists(filePath))) {
|
|
65
|
+
skipped.push(file.path);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Create directory if needed
|
|
70
|
+
await fs.mkdir(fileDir, { recursive: true });
|
|
71
|
+
|
|
72
|
+
// Write file
|
|
73
|
+
await fs.writeFile(filePath, file.content, 'utf-8');
|
|
74
|
+
written.push(file.path);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return { written, skipped };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Main setup-tagpr logic
|
|
82
|
+
*/
|
|
83
|
+
export async function setupTagpr(options: SetupTagprOptions): Promise<void> {
|
|
84
|
+
const targetDir = options.targetDir ?? process.cwd();
|
|
85
|
+
const force = options.force ?? false;
|
|
86
|
+
|
|
87
|
+
// Check if package.json exists
|
|
88
|
+
const packageJsonPath = path.join(targetDir, 'package.json');
|
|
89
|
+
if (!(await fileExists(packageJsonPath))) {
|
|
90
|
+
throw new Error('package.json not found. Are you in a project directory?');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
console.log('🔧 Setting up tagpr configuration...\n');
|
|
94
|
+
|
|
95
|
+
// Generate tagpr files
|
|
96
|
+
const files = await generateTagprSetup();
|
|
97
|
+
|
|
98
|
+
// Write files
|
|
99
|
+
const { written, skipped } = await writeFiles(targetDir, files, force);
|
|
100
|
+
|
|
101
|
+
// Report results
|
|
102
|
+
if (written.length > 0) {
|
|
103
|
+
console.log('✅ Created files:');
|
|
104
|
+
for (const file of written) {
|
|
105
|
+
console.log(` ${file}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (skipped.length > 0) {
|
|
110
|
+
console.log('\n⏭️ Skipped (already exist, use --force to overwrite):');
|
|
111
|
+
for (const file of skipped) {
|
|
112
|
+
console.log(` ${file}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log('\n🎉 Tagpr setup complete!');
|
|
117
|
+
console.log('\n📋 Required secrets:');
|
|
118
|
+
console.log(
|
|
119
|
+
' PAT_FOR_TAGPR - Personal Access Token with repo & workflow permissions',
|
|
120
|
+
);
|
|
121
|
+
console.log(' SSH_SIGNING_KEY - SSH private key for commit signing');
|
|
122
|
+
console.log(' GIT_USER_EMAIL - Email address for commits (optional)');
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function registerSetupTagprCommand(program: Command): void {
|
|
126
|
+
program
|
|
127
|
+
.command('setup-tagpr')
|
|
128
|
+
.description('Set up tagpr configuration in an existing project')
|
|
129
|
+
.option(
|
|
130
|
+
'-t, --target-dir <path>',
|
|
131
|
+
'Target directory (defaults to current directory)',
|
|
132
|
+
)
|
|
133
|
+
.option('-f, --force', 'Overwrite existing files')
|
|
134
|
+
.action(async (opts: { targetDir?: string; force?: boolean }) => {
|
|
135
|
+
try {
|
|
136
|
+
await setupTagpr({
|
|
137
|
+
targetDir: opts.targetDir,
|
|
138
|
+
force: opts.force,
|
|
139
|
+
});
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error(
|
|
142
|
+
`❌ Failed to setup tagpr: ${error instanceof Error ? error.message : String(error)}`,
|
|
143
|
+
);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
+
"extends": ["config:recommended"],
|
|
4
|
+
"packageRules": [
|
|
5
|
+
{
|
|
6
|
+
"matchUpdateTypes": ["major", "minor", "patch"],
|
|
7
|
+
"automerge": true
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"lockFileMaintenance": {
|
|
11
|
+
"enabled": true,
|
|
12
|
+
"schedule": ["before 5am on monday"]
|
|
13
|
+
}
|
|
14
|
+
}
|
package/.github/dependabot.yml
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
-
# package ecosystems to update and where the package manifests are located.
|
|
3
|
-
# Please see the documentation for all configuration options:
|
|
4
|
-
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
-
|
|
6
|
-
version: 2
|
|
7
|
-
updates:
|
|
8
|
-
- package-ecosystem: "npm" # See documentation for possible values
|
|
9
|
-
directory: "/" # Location of package manifests
|
|
10
|
-
schedule:
|
|
11
|
-
interval: "weekly"
|