@rindrics/initrepo 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -64,4 +64,6 @@ jobs:
64
64
  - run: bun install --frozen-lockfile
65
65
 
66
66
  - name: Test
67
- run: bun test
67
+ run: |
68
+ bun run build
69
+ bun test
@@ -19,3 +19,7 @@ jobs:
19
19
  - uses: Songmu/tagpr@v1
20
20
  env:
21
21
  GITHUB_TOKEN: ${{ secrets.PAT_FOR_TAGPR }}
22
+ GIT_COMMITTER_NAME: github-actions[bot]
23
+ GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
24
+ GIT_AUTHOR_NAME: github-actions[bot]
25
+ GIT_AUTHOR_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## [v0.2.0](https://github.com/Rindrics/initrepo/compare/v0.1.5...v0.2.0) - 2025-12-28
4
+ - ci: commit as GitHub Actions bot by @Rindrics in https://github.com/Rindrics/initrepo/pull/20
5
+ - feat: generate publish.yml from prepare-release command by @Rindrics in https://github.com/Rindrics/initrepo/pull/21
6
+ - feat: embed templates at build by @Rindrics in https://github.com/Rindrics/initrepo/pull/23
7
+
8
+ ## [v0.1.5](https://github.com/Rindrics/initrepo/compare/v0.1.4...v0.1.5) - 2025-12-28
9
+ - fix: correct binary name by @Rindrics in https://github.com/Rindrics/initrepo/pull/18
10
+
3
11
  ## [v0.1.4](https://github.com/Rindrics/initrepo/compare/v0.1.3...v0.1.4) - 2025-12-28
4
12
  - ci: use Node.js 24 to use npm 11.6 by @Rindrics in https://github.com/Rindrics/initrepo/pull/15
5
13
 
package/dist/cli.js CHANGED
@@ -3321,8 +3321,8 @@ var require_light = __commonJS((exports, module) => {
3321
3321
  return this.Promise.resolve();
3322
3322
  }
3323
3323
  yieldLoop(t = 0) {
3324
- return new this.Promise(function(resolve2, reject) {
3325
- return setTimeout(resolve2, t);
3324
+ return new this.Promise(function(resolve, reject) {
3325
+ return setTimeout(resolve, t);
3326
3326
  });
3327
3327
  }
3328
3328
  computePenalty() {
@@ -3533,15 +3533,15 @@ var require_light = __commonJS((exports, module) => {
3533
3533
  return this._queue.length === 0;
3534
3534
  }
3535
3535
  async _tryToRun() {
3536
- var args, cb, error, reject, resolve2, returned, task;
3536
+ var args, cb, error, reject, resolve, returned, task;
3537
3537
  if (this._running < 1 && this._queue.length > 0) {
3538
3538
  this._running++;
3539
- ({ task, args, resolve: resolve2, reject } = this._queue.shift());
3539
+ ({ task, args, resolve, reject } = this._queue.shift());
3540
3540
  cb = await async function() {
3541
3541
  try {
3542
3542
  returned = await task(...args);
3543
3543
  return function() {
3544
- return resolve2(returned);
3544
+ return resolve(returned);
3545
3545
  };
3546
3546
  } catch (error1) {
3547
3547
  error = error1;
@@ -3556,13 +3556,13 @@ var require_light = __commonJS((exports, module) => {
3556
3556
  }
3557
3557
  }
3558
3558
  schedule(task, ...args) {
3559
- var promise, reject, resolve2;
3560
- resolve2 = reject = null;
3559
+ var promise, reject, resolve;
3560
+ resolve = reject = null;
3561
3561
  promise = new this.Promise(function(_resolve, _reject) {
3562
- resolve2 = _resolve;
3562
+ resolve = _resolve;
3563
3563
  return reject = _reject;
3564
3564
  });
3565
- this._queue.push({ task, args, resolve: resolve2, reject });
3565
+ this._queue.push({ task, args, resolve, reject });
3566
3566
  this._tryToRun();
3567
3567
  return promise;
3568
3568
  }
@@ -3966,14 +3966,14 @@ var require_light = __commonJS((exports, module) => {
3966
3966
  counts = this._states.counts;
3967
3967
  return counts[0] + counts[1] + counts[2] + counts[3] === at;
3968
3968
  };
3969
- return new this.Promise((resolve2, reject) => {
3969
+ return new this.Promise((resolve, reject) => {
3970
3970
  if (finished()) {
3971
- return resolve2();
3971
+ return resolve();
3972
3972
  } else {
3973
3973
  return this.on("done", () => {
3974
3974
  if (finished()) {
3975
3975
  this.removeAllListeners("done");
3976
- return resolve2();
3976
+ return resolve();
3977
3977
  }
3978
3978
  });
3979
3979
  }
@@ -4066,9 +4066,9 @@ var require_light = __commonJS((exports, module) => {
4066
4066
  options = parser$5.load(options, this.jobDefaults);
4067
4067
  }
4068
4068
  task = (...args2) => {
4069
- return new this.Promise(function(resolve2, reject) {
4069
+ return new this.Promise(function(resolve, reject) {
4070
4070
  return fn(...args2, function(...args3) {
4071
- return (args3[0] != null ? reject : resolve2)(args3);
4071
+ return (args3[0] != null ? reject : resolve)(args3);
4072
4072
  });
4073
4073
  });
4074
4074
  };
@@ -4207,19 +4207,19 @@ var {
4207
4207
  // package.json
4208
4208
  var package_default = {
4209
4209
  name: "@rindrics/initrepo",
4210
- version: "0.1.4",
4210
+ version: "0.2.0",
4211
4211
  description: "setup GitHub repo with dev tools",
4212
4212
  type: "module",
4213
4213
  bin: {
4214
- "repo-setup": "./dist/cli.js"
4214
+ initrepo: "./dist/cli.js"
4215
4215
  },
4216
4216
  scripts: {
4217
4217
  dev: "bun run src/cli.ts",
4218
- build: "bun build src/cli.ts --outdir dist --target node",
4218
+ build: "bun scripts/embed-templates.ts && bun build src/cli.ts --outdir dist --target node",
4219
4219
  lint: "biome lint src",
4220
4220
  format: "biome format src --write",
4221
4221
  check: "biome check src",
4222
- test: "bun test",
4222
+ test: "bun scripts/embed-templates.ts && bun test",
4223
4223
  clean: "rm -rf test-* dist",
4224
4224
  prepare: "husky"
4225
4225
  },
@@ -4257,6 +4257,7 @@ import * as path from "node:path";
4257
4257
  // src/config.ts
4258
4258
  var GITHUB_ACTIONS = {
4259
4259
  "actions/checkout": "v6",
4260
+ "actions/setup-node": "v4",
4260
4261
  "Songmu/tagpr": "v1",
4261
4262
  "oven-sh/setup-bun": "v2",
4262
4263
  "github/codeql-action": "v3"
@@ -4317,8 +4318,272 @@ async function getLatestVersions(packageNames) {
4317
4318
  return versions;
4318
4319
  }
4319
4320
 
4321
+ // src/generators/embedded-templates.ts
4322
+ var EMBEDDED_TEMPLATES = {
4323
+ "common/dependabot.yml.ejs": `version: 2
4324
+ updates:
4325
+ <% if (lang === 'typescript') { -%>
4326
+ - package-ecosystem: "npm"
4327
+ directory: "/"
4328
+ schedule:
4329
+ interval: "weekly"
4330
+ <% } -%>
4331
+ - package-ecosystem: "github-actions"
4332
+ directory: "/"
4333
+ schedule:
4334
+ interval: "weekly"
4335
+ `,
4336
+ "common/release.yml.ejs": `changelog:
4337
+ exclude:
4338
+ labels:
4339
+ - tagpr
4340
+ `,
4341
+ "common/workflows/publish.yml.ejs": `name: Publish to npm
4342
+
4343
+ on:
4344
+ push:
4345
+ tags:
4346
+ - 'v*'
4347
+ workflow_dispatch:
4348
+
4349
+ permissions:
4350
+ contents: read
4351
+ id-token: write
4352
+
4353
+ jobs:
4354
+ publish:
4355
+ runs-on: ubuntu-latest
4356
+ steps:
4357
+ - uses: actions/checkout@<%= actionVersions['actions/checkout'] %>
4358
+
4359
+ - uses: oven-sh/setup-bun@<%= actionVersions['oven-sh/setup-bun'] %>
4360
+
4361
+ - uses: actions/setup-node@<%= actionVersions['actions/setup-node'] %>
4362
+ with:
4363
+ node-version: '24'
4364
+ registry-url: 'https://registry.npmjs.org'
4365
+
4366
+ - run: bun install --frozen-lockfile
4367
+
4368
+ - name: Build
4369
+ run: bun run build
4370
+
4371
+ - name: Test
4372
+ run: bun test
4373
+
4374
+ - name: Publish to npm with provenance
4375
+ run: npm publish --access public --provenance
4376
+
4377
+ `,
4378
+ "common/workflows/tagpr.yml.ejs": `name: tagpr
4379
+
4380
+ on:
4381
+ push:
4382
+ branches:
4383
+ - main
4384
+
4385
+ permissions:
4386
+ contents: write
4387
+ pull-requests: write
4388
+
4389
+ jobs:
4390
+ tagpr:
4391
+ runs-on: ubuntu-latest
4392
+ steps:
4393
+ - uses: actions/checkout@<%= actionVersions['actions/checkout'] %>
4394
+ <% if (isDevcode) { -%>
4395
+ # TODO: After replace-devcode, add token: \${{ secrets.PAT_FOR_TAGPR }}
4396
+ <% } else { -%>
4397
+ with:
4398
+ token: \${{ secrets.PAT_FOR_TAGPR }}
4399
+ <% } -%>
4400
+
4401
+ - uses: Songmu/tagpr@<%= actionVersions['Songmu/tagpr'] %>
4402
+ env:
4403
+ <% if (isDevcode) { -%>
4404
+ GITHUB_TOKEN: \${{ secrets.GITHUB_TOKEN }}
4405
+ # TODO: After replace-devcode, use PAT_FOR_TAGPR instead
4406
+ <% } else { -%>
4407
+ GITHUB_TOKEN: \${{ secrets.PAT_FOR_TAGPR }}
4408
+ GIT_COMMITTER_NAME: github-actions[bot]
4409
+ GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
4410
+ GIT_AUTHOR_NAME: github-actions[bot]
4411
+ GIT_AUTHOR_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
4412
+ <% } -%>
4413
+ `,
4414
+ "typescript/.tagpr.ejs": `# tagpr configuration
4415
+ # https://github.com/Songmu/tagpr
4416
+
4417
+ [tagpr]
4418
+ versionFile = "package.json"
4419
+ `,
4420
+ "typescript/codeql/codeql-config.yml.ejs": `name: "CodeQL config for <%= name %>"
4421
+
4422
+ paths:
4423
+ - src
4424
+
4425
+ paths-ignore:
4426
+ - node_modules/
4427
+ `,
4428
+ "typescript/package.json.ejs": `{
4429
+ "name": "<%= name %>",
4430
+ "version": "0.0.0",
4431
+ <% if (isDevcode) { -%>
4432
+ "private": true,
4433
+ <% } -%>
4434
+ "description": "",
4435
+ "type": "module",
4436
+ "scripts": {
4437
+ "dev": "bun run src/index.ts",
4438
+ "build": "bun build src/index.ts --outdir dist --target node",
4439
+ "lint": "biome lint src",
4440
+ "format": "biome format src --write",
4441
+ "check": "biome check src",
4442
+ "test": "bun test",
4443
+ "prepare": "husky"
4444
+ },
4445
+ "keywords": [],
4446
+ "author": "<%= author %>",
4447
+ "license": "MIT",
4448
+ "devDependencies": {
4449
+ "@biomejs/biome": "^<%= versions['@biomejs/biome'] %>",
4450
+ "@commitlint/cli": "^<%= versions['@commitlint/cli'] %>",
4451
+ "@commitlint/config-conventional": "^<%= versions['@commitlint/config-conventional'] %>",
4452
+ "bun-types": "^<%= versions['bun-types'] %>",
4453
+ "husky": "^<%= versions['husky'] %>",
4454
+ "typescript": "^<%= versions['typescript'] %>"
4455
+ }
4456
+ }
4457
+ `,
4458
+ "typescript/src/index.ts.ejs": `console.log('Hello from <%= name %>!');
4459
+ `,
4460
+ "typescript/tsconfig.json.ejs": `{
4461
+ "compilerOptions": {
4462
+ "target": "ES2022",
4463
+ "module": "ESNext",
4464
+ "moduleResolution": "bundler",
4465
+ "strict": true,
4466
+ "esModuleInterop": true,
4467
+ "skipLibCheck": true,
4468
+ "declaration": true,
4469
+ "outDir": "./dist",
4470
+ "rootDir": "./src",
4471
+ "types": ["bun-types"]
4472
+ },
4473
+ "include": ["src/**/*"],
4474
+ "exclude": ["node_modules", "dist"]
4475
+ }
4476
+
4477
+ `,
4478
+ "typescript/workflows/ci.yml.ejs": `name: CI
4479
+
4480
+ on:
4481
+ pull_request:
4482
+ branches:
4483
+ - main
4484
+ paths:
4485
+ - 'src/**'
4486
+ - 'package.json'
4487
+ - 'tsconfig.json'
4488
+ - 'biome.json'
4489
+ - 'bun.lock'
4490
+ - '.github/workflows/ci.yml'
4491
+ push:
4492
+ branches:
4493
+ - main
4494
+ paths:
4495
+ - 'src/**'
4496
+ - 'package.json'
4497
+ - 'tsconfig.json'
4498
+ - 'biome.json'
4499
+ - 'bun.lock'
4500
+ - '.github/workflows/ci.yml'
4501
+
4502
+ permissions:
4503
+ contents: read
4504
+
4505
+ jobs:
4506
+ check:
4507
+ runs-on: ubuntu-latest
4508
+ steps:
4509
+ - uses: actions/checkout@<%= actionVersions['actions/checkout'] %>
4510
+
4511
+ - uses: oven-sh/setup-bun@<%= actionVersions['oven-sh/setup-bun'] %>
4512
+ with:
4513
+ bun-version: latest
4514
+
4515
+ - run: bun install --frozen-lockfile
4516
+
4517
+ - name: Biome check
4518
+ run: bun run check
4519
+
4520
+ - name: TypeScript check
4521
+ run: bun run build
4522
+
4523
+ test:
4524
+ runs-on: ubuntu-latest
4525
+ steps:
4526
+ - uses: actions/checkout@<%= actionVersions['actions/checkout'] %>
4527
+
4528
+ - uses: oven-sh/setup-bun@<%= actionVersions['oven-sh/setup-bun'] %>
4529
+ with:
4530
+ bun-version: latest
4531
+
4532
+ - run: bun install --frozen-lockfile
4533
+
4534
+ - name: Test
4535
+ run: bun test
4536
+ `,
4537
+ "typescript/workflows/codeql.yml.ejs": `name: CodeQL
4538
+
4539
+ on:
4540
+ push:
4541
+ branches: [main]
4542
+ paths:
4543
+ - 'src/**'
4544
+ - 'package.json'
4545
+ - 'tsconfig.json'
4546
+ - '.github/workflows/codeql.yml'
4547
+ pull_request:
4548
+ branches: [main]
4549
+ paths:
4550
+ - 'src/**'
4551
+ - 'package.json'
4552
+ - 'tsconfig.json'
4553
+ - '.github/workflows/codeql.yml'
4554
+ schedule:
4555
+ # Run weekly on Sunday at 00:00 UTC
4556
+ - cron: '0 0 * * 0'
4557
+ workflow_dispatch:
4558
+
4559
+ jobs:
4560
+ analyze:
4561
+ name: Analyze
4562
+ runs-on: ubuntu-latest
4563
+ timeout-minutes: 10
4564
+ permissions:
4565
+ actions: read
4566
+ contents: read
4567
+ security-events: write
4568
+
4569
+ steps:
4570
+ - name: Checkout repository
4571
+ uses: actions/checkout@<%= actionVersions['actions/checkout'] %>
4572
+
4573
+ - name: Initialize CodeQL
4574
+ uses: github/codeql-action/init@<%= actionVersions['github/codeql-action'] %>
4575
+ with:
4576
+ languages: javascript-typescript
4577
+ config-file: ./.github/codeql/codeql-config.yml
4578
+
4579
+ - name: Perform CodeQL Analysis
4580
+ uses: github/codeql-action/analyze@<%= actionVersions['github/codeql-action'] %>
4581
+ with:
4582
+ category: '/language:javascript-typescript'
4583
+ `
4584
+ };
4585
+
4320
4586
  // src/generators/project.ts
4321
- var TEMPLATES_DIR = path.join(import.meta.dir, "../templates");
4322
4587
  var DEV_DEPENDENCIES = [
4323
4588
  "@biomejs/biome",
4324
4589
  "@commitlint/cli",
@@ -4338,21 +4603,11 @@ class TemplateError extends Error {
4338
4603
  this.name = "TemplateError";
4339
4604
  }
4340
4605
  }
4341
- async function loadTemplate(templatePath, data) {
4342
- const resolvedTemplatesDir = path.resolve(TEMPLATES_DIR);
4343
- const fullPath = path.resolve(TEMPLATES_DIR, templatePath);
4344
- if (!fullPath.startsWith(resolvedTemplatesDir + path.sep)) {
4345
- throw new TemplateError(`Invalid template path: "${templatePath}" resolves outside templates directory`, templatePath);
4346
- }
4347
- let template;
4348
- try {
4349
- template = await fs.readFile(fullPath, "utf-8");
4350
- } catch (error) {
4351
- const fsError = error;
4352
- if (fsError.code === "ENOENT") {
4353
- throw new TemplateError(`Template not found: "${templatePath}"`, templatePath, fsError);
4354
- }
4355
- throw new TemplateError(`Failed to read template "${templatePath}": ${fsError.message}`, templatePath, fsError);
4606
+ function loadTemplate(templatePath, data) {
4607
+ const normalizedPath = templatePath.replace(/\\/g, "/");
4608
+ const template = EMBEDDED_TEMPLATES[normalizedPath];
4609
+ if (!template) {
4610
+ throw new TemplateError(`Template not found: "${templatePath}"`, templatePath);
4356
4611
  }
4357
4612
  try {
4358
4613
  return import_ejs.default.render(template, data);
@@ -4369,7 +4624,7 @@ async function generatePackageJson(options) {
4369
4624
  ]);
4370
4625
  const author = detectedAuthor ?? "";
4371
4626
  const templatePath = `${options.lang}/package.json.ejs`;
4372
- const content = await loadTemplate(templatePath, {
4627
+ const content = loadTemplate(templatePath, {
4373
4628
  name: options.projectName,
4374
4629
  isDevcode: options.isDevcode,
4375
4630
  author,
@@ -4381,48 +4636,52 @@ async function generatePackageJson(options) {
4381
4636
  }
4382
4637
  }
4383
4638
  async function generateTsconfig(options) {
4384
- const content = await loadTemplate(`${options.lang}/tsconfig.json.ejs`, {});
4639
+ const content = loadTemplate(`${options.lang}/tsconfig.json.ejs`, {});
4385
4640
  return { path: "tsconfig.json", content };
4386
4641
  }
4387
4642
  async function generateEntryPoint(options) {
4388
- const content = await loadTemplate(`${options.lang}/src/index.ts.ejs`, {
4643
+ const content = loadTemplate(`${options.lang}/src/index.ts.ejs`, {
4389
4644
  name: options.projectName
4390
4645
  });
4391
4646
  return { path: "src/index.ts", content };
4392
4647
  }
4393
4648
  async function generateTagprConfig(options) {
4394
- const content = await loadTemplate(`${options.lang}/.tagpr.ejs`, {});
4649
+ const content = loadTemplate(`${options.lang}/.tagpr.ejs`, {});
4395
4650
  return { path: ".tagpr", content };
4396
4651
  }
4397
4652
  async function generateTagprWorkflow(options, actionVersions) {
4398
- const content = await loadTemplate("common/workflows/tagpr.yml.ejs", {
4653
+ const content = loadTemplate("common/workflows/tagpr.yml.ejs", {
4399
4654
  isDevcode: options.isDevcode,
4400
4655
  actionVersions
4401
4656
  });
4402
4657
  return { path: ".github/workflows/tagpr.yml", content };
4403
4658
  }
4404
4659
  async function generateCiWorkflow(options, actionVersions) {
4405
- const content = await loadTemplate(`${options.lang}/workflows/ci.yml.ejs`, {
4660
+ const content = loadTemplate(`${options.lang}/workflows/ci.yml.ejs`, {
4406
4661
  actionVersions
4407
4662
  });
4408
4663
  return { path: ".github/workflows/ci.yml", content };
4409
4664
  }
4410
4665
  async function generateCodeqlWorkflow(options, actionVersions) {
4411
- const content = await loadTemplate(`${options.lang}/workflows/codeql.yml.ejs`, { actionVersions });
4666
+ const content = loadTemplate(`${options.lang}/workflows/codeql.yml.ejs`, {
4667
+ actionVersions
4668
+ });
4412
4669
  return { path: ".github/workflows/codeql.yml", content };
4413
4670
  }
4414
4671
  async function generateCodeqlConfig(options) {
4415
- const content = await loadTemplate(`${options.lang}/codeql/codeql-config.yml.ejs`, { name: options.projectName });
4672
+ const content = loadTemplate(`${options.lang}/codeql/codeql-config.yml.ejs`, {
4673
+ name: options.projectName
4674
+ });
4416
4675
  return { path: ".github/codeql/codeql-config.yml", content };
4417
4676
  }
4418
4677
  async function generateDependabot(options) {
4419
- const content = await loadTemplate("common/dependabot.yml.ejs", {
4678
+ const content = loadTemplate("common/dependabot.yml.ejs", {
4420
4679
  lang: options.lang
4421
4680
  });
4422
4681
  return { path: ".github/dependabot.yml", content };
4423
4682
  }
4424
4683
  async function generateReleaseConfig() {
4425
- const content = await loadTemplate("common/release.yml.ejs", {});
4684
+ const content = loadTemplate("common/release.yml.ejs", {});
4426
4685
  return { path: ".github/release.yml", content };
4427
4686
  }
4428
4687
 
@@ -5265,8 +5524,8 @@ function withCustomRequest(customRequest) {
5265
5524
 
5266
5525
  // node_modules/@octokit/auth-token/dist-bundle/index.js
5267
5526
  var b64url = "(?:[a-zA-Z0-9_-]+)";
5268
- var sep2 = "\\.";
5269
- var jwtRE = new RegExp(`^${b64url}${sep2}${b64url}${sep2}${b64url}$`);
5527
+ var sep = "\\.";
5528
+ var jwtRE = new RegExp(`^${b64url}${sep}${b64url}${sep}${b64url}$`);
5270
5529
  var isJWT = jwtRE.test.bind(jwtRE);
5271
5530
  async function auth(token) {
5272
5531
  const isApp = isJWT(token);
@@ -8616,7 +8875,7 @@ function getCachedAuthentication(state, auth2) {
8616
8875
  return newScope === currentScope ? authentication : false;
8617
8876
  }
8618
8877
  async function wait(seconds) {
8619
- await new Promise((resolve2) => setTimeout(resolve2, seconds * 1000));
8878
+ await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
8620
8879
  }
8621
8880
  async function waitForAccessToken(request2, clientId, clientType, verification) {
8622
8881
  try {
@@ -9532,7 +9791,7 @@ async function sendRequestWithRetries(state, request2, options, createdAt, retri
9532
9791
  ++retries;
9533
9792
  const awaitTime = retries * 1000;
9534
9793
  state.log.warn(`[@octokit/auth-app] Retrying after 401 response to account for token replication delay (retry: ${retries}, wait: ${awaitTime / 1000}s)`);
9535
- await new Promise((resolve2) => setTimeout(resolve2, awaitTime));
9794
+ await new Promise((resolve) => setTimeout(resolve, awaitTime));
9536
9795
  return sendRequestWithRetries(state, request2, options, createdAt, retries);
9537
9796
  }
9538
9797
  }
@@ -11056,25 +11315,26 @@ async function replaceInCodeqlConfig(targetDir, devcode, publishName) {
11056
11315
  }
11057
11316
  async function replaceInTagprWorkflow(targetDir, _devcode, _publishName) {
11058
11317
  const workflowPath = path2.join(targetDir, ".github/workflows/tagpr.yml");
11059
- let content;
11060
11318
  try {
11061
- content = await fs2.readFile(workflowPath, "utf-8");
11062
- } catch (error) {
11063
- const fsError = error;
11064
- if (fsError.code === "ENOENT") {
11065
- return;
11066
- }
11067
- throw error;
11319
+ await fs2.access(workflowPath);
11320
+ } catch {
11321
+ return;
11068
11322
  }
11069
- content = content.replace(/GITHUB_TOKEN: \$\{\{ secrets\.GITHUB_TOKEN \}\}/g, "GITHUB_TOKEN: ${{ secrets.PAT_FOR_TAGPR }}");
11070
- content = content.replace(/(uses: actions\/checkout@v\d+)\n(\s*)# TODO: After replace-devcode, add token: \$\{\{ secrets\.PAT_FOR_TAGPR \}\}/g, `$1
11071
- $2with:
11072
- $2 token: \${{ secrets.PAT_FOR_TAGPR }}`);
11073
- content = content.replace(/\s*# TODO: After replace-devcode.*\n/g, `
11074
- `);
11075
- content = content.replace(/\n{3,}/g, `
11076
-
11077
- `);
11323
+ const actionVersions = await getLatestActionVersions();
11324
+ const content = await loadTemplate("common/workflows/tagpr.yml.ejs", {
11325
+ isDevcode: false,
11326
+ actionVersions
11327
+ });
11328
+ await fs2.writeFile(workflowPath, content, "utf-8");
11329
+ }
11330
+ async function generatePublishWorkflow(targetDir) {
11331
+ const workflowDir = path2.join(targetDir, ".github/workflows");
11332
+ const workflowPath = path2.join(workflowDir, "publish.yml");
11333
+ await fs2.mkdir(workflowDir, { recursive: true });
11334
+ const actionVersions = await getLatestActionVersions();
11335
+ const content = await loadTemplate("common/workflows/publish.yml.ejs", {
11336
+ actionVersions
11337
+ });
11078
11338
  await fs2.writeFile(workflowPath, content, "utf-8");
11079
11339
  }
11080
11340
  var MANAGED_LOCATIONS = [
@@ -11157,6 +11417,14 @@ async function prepareRelease(options) {
11157
11417
  console.log(` ⚠️ ${location.file}: ${error instanceof Error ? error.message : String(error)}`);
11158
11418
  }
11159
11419
  }
11420
+ console.log(`
11421
+ \uD83D\uDCE6 Generating release workflows:`);
11422
+ try {
11423
+ await generatePublishWorkflow(targetDir);
11424
+ console.log(" ✅ .github/workflows/publish.yml (npm OIDC publishing)");
11425
+ } catch (error) {
11426
+ console.log(` ⚠️ publish.yml: ${error instanceof Error ? error.message : String(error)}`);
11427
+ }
11160
11428
  const unmanaged = await findUnmanagedOccurrences(targetDir, devcode);
11161
11429
  if (unmanaged.length > 0) {
11162
11430
  console.log(`
@@ -11172,6 +11440,7 @@ async function prepareRelease(options) {
11172
11440
  console.log(` Package renamed: ${devcode} → ${options.publishName}`);
11173
11441
  console.log(` Private flag removed`);
11174
11442
  console.log(` Workflows updated to use PAT_FOR_TAGPR`);
11443
+ console.log(` publish.yml generated for npm OIDC publishing`);
11175
11444
  console.log(`
11176
11445
  ⚠️ Action required: Set up PAT_FOR_TAGPR secret`);
11177
11446
  console.log(` 1. Create a Personal Access Token (classic) at:`);
@@ -6,7 +6,7 @@ Accepted
6
6
 
7
7
  ## Context
8
8
 
9
- We are building `@rindrics/repo-setup`, a CLI tool that generates project scaffolding with:
9
+ We are building `@rindrics/initrepo`, a CLI tool that generates project scaffolding with:
10
10
 
11
11
  - CI/CD workflows (test, lint, tagpr, release)
12
12
  - husky configuration
@@ -0,0 +1,126 @@
1
+ # ADR 0002: Embed Templates at Build Time
2
+
3
+ ## Status
4
+
5
+ Accepted
6
+
7
+ ## Context
8
+
9
+ `@rindrics/initrepo` uses EJS templates to generate project files. Initially, templates were loaded from the filesystem at runtime:
10
+
11
+ ```typescript
12
+ const TEMPLATES_DIR = path.join(__dirname, '../templates');
13
+ const template = await fs.readFile(path.join(TEMPLATES_DIR, templatePath), 'utf-8');
14
+ ```
15
+
16
+ This approach had several issues:
17
+
18
+ 1. **Bun vs Node.js incompatibility**: The code used `import.meta.dir` (Bun-specific) to resolve template paths. When running the bundled CLI with Node.js, this API is undefined, causing `path.join()` to fail with `ERR_INVALID_ARG_TYPE`.
19
+
20
+ 2. **Distribution complexity**: Templates had to be copied alongside the bundled JavaScript (`cp -r src/templates dist/`), requiring:
21
+ - Additional build steps
22
+ - Correct `files` configuration in `package.json` for npm publishing
23
+ - Careful handling of relative paths between the bundle and templates
24
+
25
+ 3. **Runtime file system dependency**: The CLI required read access to template files at runtime, adding a potential failure point.
26
+
27
+ ## Alternatives Considered
28
+
29
+ ### 1. Runtime file loading (original approach)
30
+
31
+ Templates as separate `.ejs` files, loaded via `fs.readFile()` at runtime.
32
+
33
+ - ✅ Simple to understand
34
+ - ❌ Requires correct path resolution across Bun/Node.js
35
+ - ❌ Templates must be distributed alongside the bundle
36
+ - ❌ Runtime filesystem dependency
37
+
38
+ ### 2. Inline templates in source code
39
+
40
+ Templates as template literals directly in TypeScript source.
41
+
42
+ ```typescript
43
+ const PACKAGE_JSON_TEMPLATE = `{
44
+ "name": "<%= name %>",
45
+ ...
46
+ }`;
47
+ ```
48
+
49
+ - ✅ Single-file distribution
50
+ - ✅ No build step for templates
51
+ - ❌ Poor editing experience (no syntax highlighting for EJS/YAML)
52
+ - ❌ Difficult to review changes in PRs
53
+ - ❌ Escaping issues with backticks and `${}`
54
+
55
+ ### 3. Build-time embedding (chosen)
56
+
57
+ Templates managed as `.ejs` files, embedded into a TypeScript module at build time.
58
+
59
+ - ✅ Single-file distribution
60
+ - ✅ Best editing experience (syntax highlighting, linting)
61
+ - ✅ Clear diffs in PRs for template changes
62
+ - ✅ No runtime filesystem dependency
63
+ - ⚠️ Requires build step (`bun scripts/embed-templates.ts`)
64
+
65
+ ## Decision
66
+
67
+ Embed all EJS templates as string literals in the JavaScript bundle at build time (Alternative 3).
68
+
69
+ This is a **hybrid approach**: templates are authored as `.ejs` files for developer experience, but converted to an embedded object at build time for distribution simplicity.
70
+
71
+ ### Implementation
72
+
73
+ 1. **Build script** (`scripts/embed-templates.ts`):
74
+ - Scans `src/templates/` for `.ejs` files
75
+ - Generates `src/generators/embedded-templates.ts` with all templates as a `Record<string, string>`
76
+
77
+ 2. **Template loading** (`src/generators/project.ts`):
78
+ - Changed from async file reading to synchronous lookup in the embedded map
79
+ - Removed filesystem and path resolution logic
80
+
81
+ ```typescript
82
+ // Before (runtime loading)
83
+ export async function loadTemplate(templatePath: string, data: Record<string, unknown>): Promise<string> {
84
+ const template = await fs.readFile(path.join(TEMPLATES_DIR, templatePath), 'utf-8');
85
+ return ejs.render(template, data);
86
+ }
87
+
88
+ // After (embedded templates)
89
+ export function loadTemplate(templatePath: string, data: Record<string, unknown>): string {
90
+ const template = EMBEDDED_TEMPLATES[templatePath];
91
+ if (!template) throw new TemplateError(`Template not found: "${templatePath}"`, templatePath);
92
+ return ejs.render(template, data);
93
+ }
94
+ ```
95
+
96
+ 3. **Build process**:
97
+ ```json
98
+ "build": "bun scripts/embed-templates.ts && bun build src/cli.ts --outdir dist --target node"
99
+ ```
100
+
101
+ ### Generated File
102
+
103
+ The `embedded-templates.ts` file is auto-generated and should not be edited manually. It is gitignored and regenerated on each build.
104
+
105
+ ## Consequences
106
+
107
+ ### Positive
108
+
109
+ - **Single-file distribution**: The CLI is now a self-contained JavaScript file with no external dependencies on template files
110
+ - **Cross-runtime compatibility**: Works with both Bun and Node.js without conditional path resolution
111
+ - **Simpler npm publishing**: No need to configure `files` to include template directories
112
+ - **Faster execution**: No filesystem I/O for template loading
113
+ - **Synchronous API**: `loadTemplate()` is now synchronous, simplifying call sites
114
+
115
+ ### Negative
116
+
117
+ - **Larger bundle size**: Templates are embedded as strings in the bundle (~20KB increase)
118
+ - **Build step required**: Templates must be regenerated when changed (`bun scripts/embed-templates.ts`)
119
+ - **Generated file**: `embedded-templates.ts` is auto-generated, which may cause confusion if edited directly
120
+
121
+ ### Mitigations
122
+
123
+ - The generated file includes a clear header: `// Auto-generated by scripts/embed-templates.ts - Do not edit manually`
124
+ - `embedded-templates.ts` is added to `.gitignore` to prevent accidental commits
125
+ - The build script runs automatically as part of `pnpm build`
126
+ - Bundle size increase is negligible for a CLI tool
package/package.json CHANGED
@@ -1,18 +1,18 @@
1
1
  {
2
2
  "name": "@rindrics/initrepo",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "setup GitHub repo with dev tools",
5
5
  "type": "module",
6
6
  "bin": {
7
- "repo-setup": "./dist/cli.js"
7
+ "initrepo": "./dist/cli.js"
8
8
  },
9
9
  "scripts": {
10
10
  "dev": "bun run src/cli.ts",
11
- "build": "bun build src/cli.ts --outdir dist --target node",
11
+ "build": "bun scripts/embed-templates.ts && bun build src/cli.ts --outdir dist --target node",
12
12
  "lint": "biome lint src",
13
13
  "format": "biome format src --write",
14
14
  "check": "biome check src",
15
- "test": "bun test",
15
+ "test": "bun scripts/embed-templates.ts && bun test",
16
16
  "clean": "rm -rf test-* dist",
17
17
  "prepare": "husky"
18
18
  },
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Build script to embed EJS templates as strings in the bundle.
4
+ * This eliminates the need to distribute template files separately.
5
+ */
6
+ import * as fs from 'node:fs/promises';
7
+ import * as path from 'node:path';
8
+
9
+ const TEMPLATES_DIR = path.join(import.meta.dir, '../src/templates');
10
+ const OUTPUT_FILE = path.join(import.meta.dir, '../src/generators/embedded-templates.ts');
11
+
12
+ async function findTemplateFiles(dir: string, basePath = ''): Promise<string[]> {
13
+ const entries = await fs.readdir(dir, { withFileTypes: true });
14
+ const files: string[] = [];
15
+
16
+ for (const entry of entries) {
17
+ const relativePath = path.join(basePath, entry.name);
18
+ const fullPath = path.join(dir, entry.name);
19
+
20
+ if (entry.isDirectory()) {
21
+ files.push(...(await findTemplateFiles(fullPath, relativePath)));
22
+ } else if (entry.name.endsWith('.ejs')) {
23
+ files.push(relativePath);
24
+ }
25
+ }
26
+
27
+ return files;
28
+ }
29
+
30
+ function escapeTemplateString(content: string): string {
31
+ return content
32
+ .replace(/\\/g, '\\\\')
33
+ .replace(/`/g, '\\`')
34
+ .replace(/\$\{/g, '\\${');
35
+ }
36
+
37
+ async function main() {
38
+ const templateFiles = await findTemplateFiles(TEMPLATES_DIR);
39
+ templateFiles.sort();
40
+
41
+ const entries: string[] = [];
42
+
43
+ for (const file of templateFiles) {
44
+ const fullPath = path.join(TEMPLATES_DIR, file);
45
+ const content = await fs.readFile(fullPath, 'utf-8');
46
+ const escaped = escapeTemplateString(content);
47
+ // Normalize path separators for cross-platform compatibility
48
+ const normalizedPath = file.replace(/\\/g, '/');
49
+ entries.push(` '${normalizedPath}': \`${escaped}\``);
50
+ }
51
+
52
+ const output = `// Auto-generated by scripts/embed-templates.ts
53
+ // Do not edit manually
54
+
55
+ export const EMBEDDED_TEMPLATES: Record<string, string> = {
56
+ ${entries.join(',\n')},
57
+ };
58
+ `;
59
+
60
+ await fs.writeFile(OUTPUT_FILE, output, 'utf-8');
61
+ console.log(`Generated ${OUTPUT_FILE} with ${templateFiles.length} templates`);
62
+ }
63
+
64
+ main().catch(console.error);
@@ -1,6 +1,8 @@
1
1
  import * as fs from 'node:fs/promises';
2
2
  import * as path from 'node:path';
3
3
  import type { Command } from 'commander';
4
+ import { loadTemplate } from '../generators/project';
5
+ import { getLatestActionVersions } from '../utils/github';
4
6
 
5
7
  export interface PrepareReleaseOptions {
6
8
  /** New package name for release */
@@ -125,8 +127,8 @@ async function replaceInCodeqlConfig(
125
127
  }
126
128
 
127
129
  /**
128
- * Updates tagpr.yml: switches from GITHUB_TOKEN to PAT_FOR_TAGPR
129
- * This is a MANAGED replacement - only touches specific workflow patterns
130
+ * Updates tagpr.yml: regenerates from template with isDevcode=false
131
+ * This ensures all release-ready settings are applied
130
132
  */
131
133
  async function replaceInTagprWorkflow(
132
134
  targetDir: string,
@@ -135,34 +137,35 @@ async function replaceInTagprWorkflow(
135
137
  ): Promise<void> {
136
138
  const workflowPath = path.join(targetDir, '.github/workflows/tagpr.yml');
137
139
 
138
- let content: string;
140
+ // Check if file exists
139
141
  try {
140
- content = await fs.readFile(workflowPath, 'utf-8');
141
- } catch (error) {
142
- const fsError = error as NodeJS.ErrnoException;
143
- if (fsError.code === 'ENOENT') {
144
- return; // File doesn't exist, nothing to do
145
- }
146
- throw error;
142
+ await fs.access(workflowPath);
143
+ } catch {
144
+ return; // File doesn't exist, nothing to do
147
145
  }
148
146
 
149
- // Replace GITHUB_TOKEN with PAT_FOR_TAGPR in env
150
- content = content.replace(
151
- /GITHUB_TOKEN: \$\{\{ secrets\.GITHUB_TOKEN \}\}/g,
152
- 'GITHUB_TOKEN: ${{ secrets.PAT_FOR_TAGPR }}',
153
- );
147
+ const actionVersions = await getLatestActionVersions();
148
+ const content = await loadTemplate('common/workflows/tagpr.yml.ejs', {
149
+ isDevcode: false,
150
+ actionVersions,
151
+ });
154
152
 
155
- // Add token to checkout if not present
156
- content = content.replace(
157
- /(uses: actions\/checkout@v\d+)\n(\s*)# TODO: After replace-devcode, add token: \$\{\{ secrets\.PAT_FOR_TAGPR \}\}/g,
158
- '$1\n$2with:\n$2 token: ${{ secrets.PAT_FOR_TAGPR }}',
159
- );
153
+ await fs.writeFile(workflowPath, content, 'utf-8');
154
+ }
160
155
 
161
- // Remove remaining TODO comments
162
- content = content.replace(/\s*# TODO: After replace-devcode.*\n/g, '\n');
156
+ /**
157
+ * Generates publish.yml workflow for npm publishing with OIDC
158
+ */
159
+ async function generatePublishWorkflow(targetDir: string): Promise<void> {
160
+ const workflowDir = path.join(targetDir, '.github/workflows');
161
+ const workflowPath = path.join(workflowDir, 'publish.yml');
163
162
 
164
- // Clean up extra blank lines
165
- content = content.replace(/\n{3,}/g, '\n\n');
163
+ await fs.mkdir(workflowDir, { recursive: true });
164
+
165
+ const actionVersions = await getLatestActionVersions();
166
+ const content = await loadTemplate('common/workflows/publish.yml.ejs', {
167
+ actionVersions,
168
+ });
166
169
 
167
170
  await fs.writeFile(workflowPath, content, 'utf-8');
168
171
  }
@@ -292,6 +295,17 @@ export async function prepareRelease(
292
295
  }
293
296
  }
294
297
 
298
+ // Generate publish workflow for npm OIDC publishing
299
+ console.log('\n📦 Generating release workflows:');
300
+ try {
301
+ await generatePublishWorkflow(targetDir);
302
+ console.log(' ✅ .github/workflows/publish.yml (npm OIDC publishing)');
303
+ } catch (error) {
304
+ console.log(
305
+ ` ⚠️ publish.yml: ${error instanceof Error ? error.message : String(error)}`,
306
+ );
307
+ }
308
+
295
309
  // Scan for unmanaged occurrences
296
310
  const unmanaged = await findUnmanagedOccurrences(targetDir, devcode);
297
311
 
@@ -312,6 +326,7 @@ export async function prepareRelease(
312
326
  console.log(` Package renamed: ${devcode} → ${options.publishName}`);
313
327
  console.log(` Private flag removed`);
314
328
  console.log(` Workflows updated to use PAT_FOR_TAGPR`);
329
+ console.log(` publish.yml generated for npm OIDC publishing`);
315
330
 
316
331
  console.log(`\n⚠️ Action required: Set up PAT_FOR_TAGPR secret`);
317
332
  console.log(` 1. Create a Personal Access Token (classic) at:`);
package/src/config.ts CHANGED
@@ -4,6 +4,7 @@
4
4
  */
5
5
  export const GITHUB_ACTIONS = {
6
6
  'actions/checkout': 'v6',
7
+ 'actions/setup-node': 'v4',
7
8
  'Songmu/tagpr': 'v1',
8
9
  'oven-sh/setup-bun': 'v2',
9
10
  'github/codeql-action': 'v3',
@@ -29,26 +29,25 @@ describe('project generator', () => {
29
29
  });
30
30
 
31
31
  describe('loadTemplate', () => {
32
- test('should throw TemplateError for path traversal attempt', async () => {
33
- expect(loadTemplate('../../../etc/passwd', {})).rejects.toThrow(
32
+ test('should throw TemplateError for path traversal attempt', () => {
33
+ // With embedded templates, path traversal simply results in "not found"
34
+ expect(() => loadTemplate('../../../etc/passwd', {})).toThrow(
34
35
  TemplateError,
35
36
  );
36
- expect(loadTemplate('../../../etc/passwd', {})).rejects.toThrow(
37
- 'resolves outside templates directory',
37
+ expect(() => loadTemplate('../../../etc/passwd', {})).toThrow(
38
+ 'Template not found',
38
39
  );
39
40
  });
40
41
 
41
- test('should throw TemplateError for non-existent template', async () => {
42
- expect(loadTemplate('non-existent.ejs', {})).rejects.toThrow(
43
- TemplateError,
44
- );
45
- expect(loadTemplate('non-existent.ejs', {})).rejects.toThrow(
42
+ test('should throw TemplateError for non-existent template', () => {
43
+ expect(() => loadTemplate('non-existent.ejs', {})).toThrow(TemplateError);
44
+ expect(() => loadTemplate('non-existent.ejs', {})).toThrow(
46
45
  'Template not found',
47
46
  );
48
47
  });
49
48
 
50
- test('should load and render valid template', async () => {
51
- const result = await loadTemplate('typescript/package.json.ejs', {
49
+ test('should load and render valid template', () => {
50
+ const result = loadTemplate('typescript/package.json.ejs', {
52
51
  name: 'test',
53
52
  author: '',
54
53
  isDevcode: false,
@@ -4,8 +4,7 @@ import ejs from 'ejs';
4
4
  import type { InitOptions } from '../types';
5
5
  import { getLatestActionVersions } from '../utils/github';
6
6
  import { getLatestVersions, getNpmUsername } from '../utils/npm';
7
-
8
- const TEMPLATES_DIR = path.join(import.meta.dir, '../templates');
7
+ import { EMBEDDED_TEMPLATES } from './embedded-templates';
9
8
 
10
9
  const DEV_DEPENDENCIES = [
11
10
  '@biomejs/biome',
@@ -32,39 +31,21 @@ export class TemplateError extends Error {
32
31
  }
33
32
  }
34
33
 
35
- export async function loadTemplate(
34
+ export function loadTemplate(
36
35
  templatePath: string,
37
36
  data: Record<string, unknown>,
38
- ): Promise<string> {
39
- const resolvedTemplatesDir = path.resolve(TEMPLATES_DIR);
40
- const fullPath = path.resolve(TEMPLATES_DIR, templatePath);
37
+ ): string {
38
+ // Normalize path separators
39
+ const normalizedPath = templatePath.replace(/\\/g, '/');
41
40
 
42
- if (!fullPath.startsWith(resolvedTemplatesDir + path.sep)) {
41
+ const template = EMBEDDED_TEMPLATES[normalizedPath];
42
+ if (!template) {
43
43
  throw new TemplateError(
44
- `Invalid template path: "${templatePath}" resolves outside templates directory`,
44
+ `Template not found: "${templatePath}"`,
45
45
  templatePath,
46
46
  );
47
47
  }
48
48
 
49
- let template: string;
50
- try {
51
- template = await fs.readFile(fullPath, 'utf-8');
52
- } catch (error) {
53
- const fsError = error as NodeJS.ErrnoException;
54
- if (fsError.code === 'ENOENT') {
55
- throw new TemplateError(
56
- `Template not found: "${templatePath}"`,
57
- templatePath,
58
- fsError,
59
- );
60
- }
61
- throw new TemplateError(
62
- `Failed to read template "${templatePath}": ${fsError.message}`,
63
- templatePath,
64
- fsError,
65
- );
66
- }
67
-
68
49
  try {
69
50
  return ejs.render(template, data);
70
51
  } catch (error) {
@@ -87,7 +68,7 @@ export async function generatePackageJson(
87
68
  ]);
88
69
  const author = detectedAuthor ?? '';
89
70
  const templatePath = `${options.lang}/package.json.ejs`;
90
- const content = await loadTemplate(templatePath, {
71
+ const content = loadTemplate(templatePath, {
91
72
  name: options.projectName,
92
73
  isDevcode: options.isDevcode,
93
74
  author,
@@ -104,14 +85,14 @@ export async function generatePackageJson(
104
85
  export async function generateTsconfig(
105
86
  options: InitOptions,
106
87
  ): Promise<GeneratedFile> {
107
- const content = await loadTemplate(`${options.lang}/tsconfig.json.ejs`, {});
88
+ const content = loadTemplate(`${options.lang}/tsconfig.json.ejs`, {});
108
89
  return { path: 'tsconfig.json', content };
109
90
  }
110
91
 
111
92
  export async function generateEntryPoint(
112
93
  options: InitOptions,
113
94
  ): Promise<GeneratedFile> {
114
- const content = await loadTemplate(`${options.lang}/src/index.ts.ejs`, {
95
+ const content = loadTemplate(`${options.lang}/src/index.ts.ejs`, {
115
96
  name: options.projectName,
116
97
  });
117
98
  return { path: 'src/index.ts', content };
@@ -120,7 +101,7 @@ export async function generateEntryPoint(
120
101
  export async function generateTagprConfig(
121
102
  options: InitOptions,
122
103
  ): Promise<GeneratedFile> {
123
- const content = await loadTemplate(`${options.lang}/.tagpr.ejs`, {});
104
+ const content = loadTemplate(`${options.lang}/.tagpr.ejs`, {});
124
105
  return { path: '.tagpr', content };
125
106
  }
126
107
 
@@ -128,7 +109,7 @@ export async function generateTagprWorkflow(
128
109
  options: InitOptions,
129
110
  actionVersions: Record<string, string>,
130
111
  ): Promise<GeneratedFile> {
131
- const content = await loadTemplate('common/workflows/tagpr.yml.ejs', {
112
+ const content = loadTemplate('common/workflows/tagpr.yml.ejs', {
132
113
  isDevcode: options.isDevcode,
133
114
  actionVersions,
134
115
  });
@@ -139,7 +120,7 @@ export async function generateCiWorkflow(
139
120
  options: InitOptions,
140
121
  actionVersions: Record<string, string>,
141
122
  ): Promise<GeneratedFile> {
142
- const content = await loadTemplate(`${options.lang}/workflows/ci.yml.ejs`, {
123
+ const content = loadTemplate(`${options.lang}/workflows/ci.yml.ejs`, {
143
124
  actionVersions,
144
125
  });
145
126
  return { path: '.github/workflows/ci.yml', content };
@@ -149,34 +130,32 @@ export async function generateCodeqlWorkflow(
149
130
  options: InitOptions,
150
131
  actionVersions: Record<string, string>,
151
132
  ): Promise<GeneratedFile> {
152
- const content = await loadTemplate(
153
- `${options.lang}/workflows/codeql.yml.ejs`,
154
- { actionVersions },
155
- );
133
+ const content = loadTemplate(`${options.lang}/workflows/codeql.yml.ejs`, {
134
+ actionVersions,
135
+ });
156
136
  return { path: '.github/workflows/codeql.yml', content };
157
137
  }
158
138
 
159
139
  export async function generateCodeqlConfig(
160
140
  options: InitOptions,
161
141
  ): Promise<GeneratedFile> {
162
- const content = await loadTemplate(
163
- `${options.lang}/codeql/codeql-config.yml.ejs`,
164
- { name: options.projectName },
165
- );
142
+ const content = loadTemplate(`${options.lang}/codeql/codeql-config.yml.ejs`, {
143
+ name: options.projectName,
144
+ });
166
145
  return { path: '.github/codeql/codeql-config.yml', content };
167
146
  }
168
147
 
169
148
  export async function generateDependabot(
170
149
  options: InitOptions,
171
150
  ): Promise<GeneratedFile> {
172
- const content = await loadTemplate('common/dependabot.yml.ejs', {
151
+ const content = loadTemplate('common/dependabot.yml.ejs', {
173
152
  lang: options.lang,
174
153
  });
175
154
  return { path: '.github/dependabot.yml', content };
176
155
  }
177
156
 
178
157
  export async function generateReleaseConfig(): Promise<GeneratedFile> {
179
- const content = await loadTemplate('common/release.yml.ejs', {});
158
+ const content = loadTemplate('common/release.yml.ejs', {});
180
159
  return { path: '.github/release.yml', content };
181
160
  }
182
161
 
@@ -0,0 +1,36 @@
1
+ name: Publish to npm
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+ workflow_dispatch:
8
+
9
+ permissions:
10
+ contents: read
11
+ id-token: write
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - uses: actions/checkout@<%= actionVersions['actions/checkout'] %>
18
+
19
+ - uses: oven-sh/setup-bun@<%= actionVersions['oven-sh/setup-bun'] %>
20
+
21
+ - uses: actions/setup-node@<%= actionVersions['actions/setup-node'] %>
22
+ with:
23
+ node-version: '24'
24
+ registry-url: 'https://registry.npmjs.org'
25
+
26
+ - run: bun install --frozen-lockfile
27
+
28
+ - name: Build
29
+ run: bun run build
30
+
31
+ - name: Test
32
+ run: bun test
33
+
34
+ - name: Publish to npm with provenance
35
+ run: npm publish --access public --provenance
36
+
@@ -28,4 +28,8 @@ jobs:
28
28
  # TODO: After replace-devcode, use PAT_FOR_TAGPR instead
29
29
  <% } else { -%>
30
30
  GITHUB_TOKEN: ${{ secrets.PAT_FOR_TAGPR }}
31
+ GIT_COMMITTER_NAME: github-actions[bot]
32
+ GIT_COMMITTER_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
33
+ GIT_AUTHOR_NAME: github-actions[bot]
34
+ GIT_AUTHOR_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
31
35
  <% } -%>