@theholocron/cli 2.0.0-alpha.7 → 2.0.0-alpha.9

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +1225 -236
  2. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -446,6 +446,1201 @@ async function runNpmBumpVersions(input) {
446
446
  };
447
447
  }
448
448
  //#endregion
449
+ //#region src/templates/index.ts
450
+ /**
451
+ * All reusable workflow and composite action content bundled as string
452
+ * constants so the CLI can push them to theholocron/.github without
453
+ * needing filesystem access at runtime.
454
+ *
455
+ * These are the sources of truth — the YAML files in this directory
456
+ * have been removed in favour of these constants.
457
+ */
458
+ const ACTIONS = {
459
+ "setup/action": `\
460
+ name: Setup
461
+ description: Prepare the environment and install project dependencies.
462
+
463
+ inputs:
464
+ node-version:
465
+ description: Node.js version
466
+ required: false
467
+ default: "22.x"
468
+
469
+ pnpm-version:
470
+ description: pnpm version
471
+ required: false
472
+ default: "10"
473
+
474
+ runs:
475
+ using: composite
476
+
477
+ steps:
478
+ - uses: ./.github/actions/setup-node
479
+ with:
480
+ node-version: \${{ inputs.node-version }}
481
+ pnpm-version: \${{ inputs.pnpm-version }}
482
+
483
+ - uses: ./.github/actions/install
484
+ `,
485
+ "install/action": `\
486
+ name: Install dependencies
487
+ description: Install project dependencies with pnpm frozen lockfile.
488
+
489
+ runs:
490
+ using: composite
491
+
492
+ steps:
493
+ - name: Install dependencies
494
+ shell: bash
495
+ run: pnpm install --frozen-lockfile
496
+ `,
497
+ "setup-node/action": `\
498
+ name: Setup Node
499
+ description: Install pnpm and Node.js with pnpm dependency caching.
500
+
501
+ inputs:
502
+ node-version:
503
+ description: Node.js version
504
+ required: false
505
+ default: "22.x"
506
+
507
+ pnpm-version:
508
+ description: pnpm version
509
+ required: false
510
+ default: "10"
511
+
512
+ runs:
513
+ using: composite
514
+
515
+ steps:
516
+ - name: Setup pnpm
517
+ uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
518
+ with:
519
+ version: \${{ inputs.pnpm-version }}
520
+
521
+ - name: Setup Node.js
522
+ uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
523
+ with:
524
+ node-version: \${{ inputs.node-version }}
525
+ cache: pnpm
526
+ `
527
+ };
528
+ const REUSABLE_WORKFLOWS = {
529
+ audit: `\
530
+ name: Audit
531
+
532
+ on: # yamllint disable-line rule:truthy
533
+ workflow_call:
534
+ inputs:
535
+ build-script:
536
+ description: Script to build before analyzing bundle size
537
+ type: string
538
+ required: false
539
+ default: pnpm build
540
+ secrets:
541
+ BUNDLEWATCH_GITHUB_TOKEN:
542
+ required: true
543
+
544
+ jobs:
545
+ bundle-size:
546
+ name: Audit the bundle size
547
+ permissions:
548
+ contents: read
549
+ runs-on: ubuntu-latest
550
+ timeout-minutes: 15
551
+ concurrency:
552
+ group: audit-\${{ github.ref }}
553
+ cancel-in-progress: true
554
+ steps:
555
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
556
+ name: Checkout repository
557
+ with:
558
+ fetch-depth: 0
559
+
560
+ - uses: theholocron/.github/.github/actions/setup@main
561
+ name: Setup
562
+
563
+ - uses: jackyef/bundlewatch-gh-action@01f51133d3580a6daa046ca83eb233d79735e1c1 # 0.3.0
564
+ name: Analyze using BundleWatch
565
+ with:
566
+ build-script: \${{ inputs.build-script }}
567
+ bundlewatch-github-token: \${{ secrets.BUNDLEWATCH_GITHUB_TOKEN }}
568
+ `,
569
+ "bookkeeping-pr": `\
570
+ name: PR Bookkeeping
571
+
572
+ on: # yamllint disable-line rule:truthy
573
+ workflow_call:
574
+ inputs:
575
+ configuration-path:
576
+ description: Path to the labeler configuration file in the calling repo
577
+ type: string
578
+ required: false
579
+ default: .github/labeler.yml
580
+
581
+ jobs:
582
+ label:
583
+ name: Add Labels to PRs
584
+ permissions:
585
+ contents: read
586
+ pull-requests: write
587
+ runs-on: ubuntu-latest
588
+ timeout-minutes: 5
589
+ concurrency:
590
+ group: bookkeeping-pr-\${{ github.event.pull_request.number }}
591
+ cancel-in-progress: true
592
+ steps:
593
+ - uses: github/issue-labeler@c1b0f9f52a63158c4adc09425e858e87b32e9685 # v3.4
594
+ with:
595
+ # Fall back to default path when triggered directly (not via workflow_call)
596
+ # because inputs.* defaults only apply on workflow_call events.
597
+ configuration-path: \${{ inputs.configuration-path || '.github/labeler.yml' }}
598
+ include-title: 1
599
+ include-body: 0
600
+ sync-labels: 1
601
+ enable-versioned-regex: 0
602
+ repo-token: \${{ github.token }}
603
+ `,
604
+ codeql: `\
605
+ name: CodeQL
606
+
607
+ on: # yamllint disable-line rule:truthy
608
+ workflow_call:
609
+ inputs:
610
+ language:
611
+ description: CodeQL language to analyze
612
+ type: string
613
+ required: false
614
+ default: javascript-typescript
615
+
616
+ jobs:
617
+ analyze:
618
+ name: Analyze (\${{ inputs.language }})
619
+ permissions:
620
+ actions: read
621
+ contents: read
622
+ security-events: write
623
+ runs-on: ubuntu-latest
624
+ timeout-minutes: 45
625
+ # Do not cancel in-progress security scans.
626
+ concurrency:
627
+ group: codeql-\${{ github.ref }}
628
+ cancel-in-progress: false
629
+ steps:
630
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
631
+ name: Checkout repository
632
+
633
+ - uses: github/codeql-action/init@411c4c9a36b3fca4d674f06b6396b2c6d23522c6 # v3
634
+ name: Initialize CodeQL
635
+ with:
636
+ languages: \${{ inputs.language }}
637
+
638
+ - uses: github/codeql-action/autobuild@411c4c9a36b3fca4d674f06b6396b2c6d23522c6 # v3
639
+ name: Autobuild
640
+
641
+ - uses: github/codeql-action/analyze@411c4c9a36b3fca4d674f06b6396b2c6d23522c6 # v3
642
+ name: Analyze
643
+ with:
644
+ category: /language:\${{ inputs.language }}
645
+ `,
646
+ dependencies: `\
647
+ name: Dependencies
648
+
649
+ on: # yamllint disable-line rule:truthy
650
+ workflow_call:
651
+ secrets:
652
+ merge-token:
653
+ description: >
654
+ Optional privileged token for auto-merge. Falls back to GITHUB_TOKEN.
655
+ Required when branch protection enforces required reviews — GITHUB_TOKEN
656
+ cannot approve its own PRs.
657
+ required: false
658
+
659
+ jobs:
660
+ dependabot:
661
+ name: Update the dependencies
662
+ permissions:
663
+ contents: write
664
+ pull-requests: write
665
+ runs-on: ubuntu-latest
666
+ timeout-minutes: 5
667
+ concurrency:
668
+ group: dependencies-\${{ github.event.pull_request.number }}
669
+ cancel-in-progress: true
670
+ if: github.event.pull_request.user.login == 'dependabot[bot]'
671
+ steps:
672
+ - uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2
673
+ name: Fetch Dependabot metadata
674
+ id: metadata
675
+
676
+ - run: gh pr merge --auto --squash "$PR_URL"
677
+ # --squash is intentional: repoPolicy sets allow_merge_commit: false,
678
+ # so --merge would fail on any repo using the standard preset.
679
+ name: Enable auto-merge for Dependabot PRs
680
+ if: steps.metadata.outputs.update-type == 'version-update:semver-patch'
681
+ env:
682
+ PR_URL: \${{ github.event.pull_request.html_url }}
683
+ GH_TOKEN: \${{ secrets.merge-token || github.token }}
684
+ `,
685
+ greetings: `\
686
+ name: Greetings
687
+
688
+ on: # yamllint disable-line rule:truthy
689
+ workflow_call:
690
+
691
+ jobs:
692
+ greeting:
693
+ name: Greet first-time contributors
694
+ permissions:
695
+ issues: write
696
+ pull-requests: write
697
+ runs-on: ubuntu-latest
698
+ timeout-minutes: 5
699
+ # Group by the issue/PR number so duplicate events don't race each other.
700
+ concurrency:
701
+ group: greetings-\${{ github.event.issue.number || github.event.pull_request.number }}
702
+ cancel-in-progress: false
703
+ steps:
704
+ - uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7
705
+ name: Greet on first contribution
706
+ with:
707
+ script: |
708
+ // Only greet on the initial open — ignore synchronize, reopened, etc.
709
+ if (context.payload.action !== 'opened') return;
710
+
711
+ const actor = context.actor;
712
+ const { owner, repo } = context.repo;
713
+
714
+ // Payload inspection is more reliable than context.eventName for detecting
715
+ // whether this is an issue vs. PR event — works regardless of how GitHub
716
+ // propagates event names through workflow_call chains.
717
+ const isIssue = !!context.payload.issue && !context.payload.pull_request;
718
+ // listForRepo returns both issues and PRs (GitHub treats PRs as issues),
719
+ // sorted newest-first. Filter by type to track first-issue and first-PR
720
+ // independently, and avoid search-index eventual-consistency lag.
721
+ const { data: recent } = await github.rest.issues.listForRepo({
722
+ owner, repo,
723
+ creator: actor,
724
+ state: 'all',
725
+ per_page: 100
726
+ });
727
+
728
+ const sameType = recent.filter(item =>
729
+ isIssue ? !item.pull_request : !!item.pull_request
730
+ );
731
+
732
+ if (sameType.length !== 1) return;
733
+ const body = isIssue
734
+ ? \`Hey @\${actor}!\\n\\nWe really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we are a limited number of volunteers, so it is possible that this will not be addressed as swiftly.\\n\\nYour patience is much appreciated and we will get back to you as quickly as possible.\`
735
+ : \`Hey @\${actor}!\\n\\nWe really appreciate you taking the time to help out with this PR. The collaborators on this project attempt to help as many people as possible, but we are a limited number of volunteers, so it is possible that this will not be addressed as swiftly.\\n\\nYour patience is much appreciated and we will get back to you as quickly as possible.\`;
736
+
737
+ await github.rest.issues.createComment({
738
+ owner,
739
+ repo,
740
+ issue_number: context.issue.number,
741
+ body
742
+ });
743
+ `,
744
+ lint: `\
745
+ name: Lint
746
+
747
+ on: # yamllint disable-line rule:truthy
748
+ workflow_call:
749
+ inputs:
750
+ prettier-config:
751
+ type: string
752
+ required: false
753
+ default: prettier.config.js
754
+ yaml-config:
755
+ type: string
756
+ required: false
757
+ default: yamllint.config.yml
758
+ enable-auto-commit:
759
+ description: Auto-commit super-linter fixes via GPG-signed commit
760
+ type: boolean
761
+ required: false
762
+ default: false
763
+ secrets:
764
+ SUPER_LINTER_GPG_PRIVATE_KEY:
765
+ required: false
766
+ SUPER_LINTER_GPG_PASSPHRASE:
767
+ required: false
768
+
769
+ jobs:
770
+ super-lint:
771
+ name: Lint entire codebase
772
+ permissions:
773
+ contents: write
774
+ statuses: write
775
+ runs-on: ubuntu-latest
776
+ timeout-minutes: 30
777
+ steps:
778
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
779
+ name: Checkout repository
780
+ with:
781
+ fetch-depth: 0
782
+ token: \${{ github.token }}
783
+
784
+ - uses: theholocron/.github/.github/actions/setup@main
785
+ name: Setup
786
+
787
+ - uses: super-linter/super-linter/slim@b92721f792f381cedc002ecdbb9847a15ece5bb8 # v7.1.0
788
+ name: Run Super Linter
789
+ env:
790
+ GITHUB_TOKEN: \${{ github.token }}
791
+ ANNOTATE_ONLY: true
792
+ DISABLE_COMMENTS: false
793
+ IGNORE_GITIGNORED_FILES: true
794
+ LINTER_RULES_PATH: /
795
+ EDITORCONFIG_FILE_NAME: ".editorconfig-checker.json"
796
+ FIX_ENV: true
797
+ FIX_GRAPHQL_PRETTIER: true
798
+ FIX_HTML_PRETTIER: true
799
+ FIX_JAVASCRIPT_PRETTIER: true
800
+ FIX_JSX_PRETTIER: true
801
+ FIX_MARKDOWN_PRETTIER: true
802
+ FIX_TSX: true
803
+ FIX_TYPESCRIPT_PRETTIER: true
804
+ PRETTIER_CONFIG: \${{ inputs.prettier-config }}
805
+ VALIDATE_DOCKERFILE: true
806
+ VALIDATE_EDITORCONFIG: true
807
+ VALIDATE_ENV: true
808
+ VALIDATE_GIT_COMMITLINT: true
809
+ VALIDATE_GIT_MERGE_CONFLICT_MARKERS: true
810
+ VALIDATE_GITHUB_ACTIONS: true
811
+ VALIDATE_GITLEAKS: true
812
+ VALIDATE_GRAPHQL_PRETTIER: true
813
+ VALIDATE_HTML_PRETTIER: true
814
+ VALIDATE_JAVASCRIPT_PRETTIER: true
815
+ VALIDATE_JSX_PRETTIER: true
816
+ VALIDATE_MARKDOWN_PRETTIER: true
817
+ VALIDATE_TSX: true
818
+ VALIDATE_TYPESCRIPT_PRETTIER: true
819
+ VALIDATE_YAML: true
820
+ YAML_CONFIG_FILE: \${{ inputs.yaml-config }}
821
+
822
+ - uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6
823
+ name: Import GPG Key
824
+ # Conditions mirror auto-commit exactly — no point importing GPG if the
825
+ # commit step will be skipped (fork PR, default branch, or secret unset).
826
+ if: >
827
+ inputs.enable-auto-commit == true &&
828
+ github.event.pull_request != null &&
829
+ github.event.pull_request.head.repo.full_name == github.repository &&
830
+ github.ref_name != github.event.repository.default_branch &&
831
+ secrets.SUPER_LINTER_GPG_PRIVATE_KEY != ''
832
+ with:
833
+ git_user_signingkey: true
834
+ git_commit_gpgsign: true
835
+ GPG_PRIVATE_KEY: \${{ secrets.SUPER_LINTER_GPG_PRIVATE_KEY }}
836
+ PASSPHRASE: \${{ secrets.SUPER_LINTER_GPG_PASSPHRASE }}
837
+
838
+ - uses: stefanzweifel/git-auto-commit-action@b863ae1933cb653a53c021fe36dbb774e1fb9403 # v5
839
+ name: Commit and push linting fixes
840
+ if: >
841
+ inputs.enable-auto-commit == true &&
842
+ github.event.pull_request != null &&
843
+ github.event.pull_request.head.repo.full_name == github.repository &&
844
+ github.ref_name != github.event.repository.default_branch &&
845
+ secrets.SUPER_LINTER_GPG_PRIVATE_KEY != ''
846
+ with:
847
+ branch: \${{ github.event.pull_request.head.ref || github.head_ref || github.ref }}
848
+ commit_message: "chore: fix linting issues"
849
+ commit_options: "--no-verify --signoff"
850
+ commit_user_name: super-linter
851
+ commit_user_email: super-linter@super-linter.dev
852
+ `,
853
+ release: `\
854
+ name: Release
855
+
856
+ # Semantic-release with OIDC Trusted Publishing — no NPM_TOKEN required.
857
+ # The calling repo must have a .releaserc.json that configures branches,
858
+ # plugins, and any publish options. npm@11+ is installed to support OIDC.
859
+
860
+ on: # yamllint disable-line rule:truthy
861
+ workflow_call:
862
+ inputs:
863
+ run-build:
864
+ description: Run \`pnpm build\` before releasing
865
+ type: boolean
866
+ required: false
867
+ default: true
868
+
869
+ jobs:
870
+ release:
871
+ name: Semantic release
872
+ permissions:
873
+ contents: write
874
+ id-token: write
875
+ issues: write
876
+ pull-requests: write
877
+ runs-on: ubuntu-latest
878
+ timeout-minutes: 30
879
+ # Do not cancel in-progress releases — a partial release is worse than a slow one.
880
+ concurrency:
881
+ group: release-\${{ github.ref }}
882
+ cancel-in-progress: false
883
+ steps:
884
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
885
+ name: Checkout repository
886
+ with:
887
+ fetch-depth: 0
888
+ token: \${{ github.token }}
889
+
890
+ - uses: theholocron/.github/.github/actions/setup@main
891
+ name: Setup
892
+
893
+ - run: npm install -g npm@11 sigstore
894
+ name: Upgrade npm for OIDC support
895
+ # sigstore is required by libnpmpublish/provenance.js at module parse
896
+ # time — before any config takes effect. Some npm 11.x builds stopped
897
+ # bundling it; installing it globally into the same prefix ensures it
898
+ # resolves regardless of npm version. (Discovered 2026-07-09.)
899
+
900
+ - run: pnpm build
901
+ name: Build
902
+ if: \${{ inputs.run-build == true }}
903
+
904
+ - run: npx semantic-release
905
+ name: Release
906
+ env:
907
+ GITHUB_TOKEN: \${{ github.token }}
908
+ NPM_CONFIG_PROVENANCE: true
909
+ `,
910
+ review: `\
911
+ name: Review
912
+
913
+ # ReviewDog is the annotation layer — posts inline PR diff annotations.
914
+ # Runs on pull_request only: inline annotations require PR context,
915
+ # and branch protection ensures all changes go through PRs anyway.
916
+ # super-linter (lint.yml) is the CI gate covering push + PR events.
917
+ # Gitleaks and YAML are intentionally duplicated: super-linter gates
918
+ # merges; ReviewDog surfaces exact line annotations in the PR diff.
919
+
920
+ on: # yamllint disable-line rule:truthy
921
+ workflow_call:
922
+
923
+ concurrency:
924
+ group: review-\${{ github.workflow }}-\${{ github.ref }}
925
+ cancel-in-progress: true
926
+
927
+ jobs:
928
+ reviewdog:
929
+ name: Review PRs
930
+ runs-on: ubuntu-latest
931
+ timeout-minutes: 20
932
+ permissions:
933
+ contents: read
934
+ checks: write
935
+ pull-requests: write
936
+
937
+ steps:
938
+ - name: Checkout repository
939
+ uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
940
+
941
+ - name: Setup
942
+ if: \${{ hashFiles('pnpm-lock.yaml') != '' }}
943
+ uses: theholocron/.github/.github/actions/setup@main
944
+
945
+ - name: Install ReviewDog
946
+ uses: reviewdog/action-setup@d8a7baabd7f3e8544ee4dbde3ee41d0011c3a93f # v1
947
+ with:
948
+ reviewdog_version: latest
949
+
950
+ #
951
+ # Always applicable
952
+ #
953
+
954
+ - name: Gitleaks (secrets)
955
+ uses: reviewdog/action-gitleaks@2b7b5685e3e3eecddab5d30cfa04f18123031421 # v1
956
+ with:
957
+ reporter: github-pr-check
958
+
959
+ - name: YamlLint
960
+ uses: reviewdog/action-yamllint@b5f7217d8c815ae374d1d55840d5e569d82f01f0 # v1
961
+ with:
962
+ reporter: github-pr-check
963
+ yamllint_flags: >-
964
+ \${{ hashFiles('yamllint.config.yml') != ''
965
+ && format('-c {0}/yamllint.config.yml {0}', github.workspace)
966
+ || github.workspace }}
967
+
968
+ - name: ActionLint (GitHub Actions)
969
+ if: \${{ hashFiles('.github/workflows/*.yml', '.github/workflows/*.yaml') != '' }}
970
+ uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1
971
+ with:
972
+ reporter: github-pr-check
973
+
974
+ #
975
+ # TypeScript / JavaScript
976
+ #
977
+
978
+ - name: ESLint
979
+ if: >-
980
+ \${{
981
+ hashFiles(
982
+ '**/eslint.config.js',
983
+ '**/eslint.config.mjs',
984
+ '**/eslint.config.cjs',
985
+ '**/eslint.config.ts',
986
+ '**/.eslintrc',
987
+ '**/.eslintrc.js',
988
+ '**/.eslintrc.cjs',
989
+ '**/.eslintrc.json',
990
+ '**/.eslintrc.yaml',
991
+ '**/.eslintrc.yml'
992
+ ) != ''
993
+ }}
994
+ uses: reviewdog/action-eslint@556a3fdaf8b4201d4d74d406013386aa4f7dab96 # v1
995
+ with:
996
+ reporter: github-pr-check
997
+ eslint_flags: .
998
+
999
+ - name: TypeScript
1000
+ if: \${{ hashFiles('**/tsconfig.json') != '' }}
1001
+ uses: EPMatt/reviewdog-action-tsc@63d923a3c5b4497671940b8874f58a404e2351b5 # v1
1002
+ with:
1003
+ reporter: github-pr-check
1004
+
1005
+ #
1006
+ # Shell
1007
+ #
1008
+
1009
+ - name: ShellCheck
1010
+ if: \${{ hashFiles('**/*.sh') != '' }}
1011
+ uses: reviewdog/action-shellcheck@4c07458293ac342d477251099501a718ae5ef86e # v1
1012
+ with:
1013
+ reporter: github-pr-check
1014
+ fail_level: none
1015
+
1016
+ #
1017
+ # Docker
1018
+ #
1019
+
1020
+ - name: Hadolint
1021
+ if: >-
1022
+ \${{
1023
+ hashFiles(
1024
+ '**/Dockerfile',
1025
+ '**/*.Dockerfile',
1026
+ '**/Containerfile'
1027
+ ) != ''
1028
+ }}
1029
+ uses: reviewdog/action-hadolint@1b2cfa6ba72072ad35158d7ff3aa49bbdc03506d # v1
1030
+ with:
1031
+ reporter: github-pr-check
1032
+ fail_level: none
1033
+
1034
+ #
1035
+ # Environment files
1036
+ #
1037
+
1038
+ - name: dotenv-linter
1039
+ if: \${{ hashFiles('**/.env*') != '' }}
1040
+ uses: dotenv-linter/action-dotenv-linter@21287e2624aaf2dc8da5dd8ccfe8e49c63501116 # v2
1041
+ with:
1042
+ reporter: github-code-suggestions
1043
+
1044
+ #
1045
+ # Documentation
1046
+ #
1047
+
1048
+ - name: Alex (inclusive language)
1049
+ if: \${{ hashFiles('**/*.md') != '' }}
1050
+ uses: reviewdog/action-alex@347481655add010a2ae302df34b57c9bcfa0d6e4 # v1
1051
+ with:
1052
+ reporter: github-pr-check
1053
+ `,
1054
+ stale: `\
1055
+ name: Stale
1056
+
1057
+ on: # yamllint disable-line rule:truthy
1058
+ workflow_call:
1059
+ inputs:
1060
+ days-before-stale:
1061
+ description: Days of inactivity before an issue is marked stale
1062
+ type: number
1063
+ required: false
1064
+ default: 30
1065
+ days-before-close:
1066
+ description: Days of inactivity after stale label before closing
1067
+ type: number
1068
+ required: false
1069
+ default: 5
1070
+
1071
+ jobs:
1072
+ stale:
1073
+ name: Mark stale issues and pull requests
1074
+ permissions:
1075
+ contents: write
1076
+ issues: write
1077
+ pull-requests: write
1078
+ runs-on: ubuntu-latest
1079
+ timeout-minutes: 10
1080
+ steps:
1081
+ - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9
1082
+ name: Run Stale
1083
+ with:
1084
+ close-issue-message: >
1085
+ This issue was closed because it has been stalled for
1086
+ \${{ inputs.days-before-close }} days with no activity.
1087
+ days-before-close: \${{ inputs.days-before-close }}
1088
+ days-before-stale: \${{ inputs.days-before-stale }}
1089
+ exempt-all-pr-milestones: true
1090
+ stale-issue-label: wontfix
1091
+ stale-issue-message: >
1092
+ This issue is stale because it has been open \${{ inputs.days-before-stale }}
1093
+ days with no activity. Remove the stale label or comment, or this will be
1094
+ closed in \${{ inputs.days-before-close }} days.
1095
+ stale-pr-label: wontfix
1096
+ stale-pr-message: >
1097
+ This PR is stale because it has been open \${{ inputs.days-before-stale }}
1098
+ days with no activity. Remove the stale label or comment, or this will be
1099
+ closed in \${{ inputs.days-before-close }} days.
1100
+ `,
1101
+ test: `\
1102
+ name: Test
1103
+
1104
+ on: # yamllint disable-line rule:truthy
1105
+ workflow_call:
1106
+ secrets:
1107
+ CODECOV_TOKEN:
1108
+ required: false
1109
+
1110
+ jobs:
1111
+ unit:
1112
+ name: Run tests and collect coverage
1113
+ permissions:
1114
+ contents: read
1115
+ runs-on: ubuntu-latest
1116
+ timeout-minutes: 15
1117
+ steps:
1118
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
1119
+ name: Checkout repository
1120
+ with:
1121
+ fetch-depth: 0
1122
+
1123
+ - uses: theholocron/.github/.github/actions/setup@main
1124
+ name: Setup
1125
+
1126
+ - run: pnpm test -- --coverage
1127
+ name: Run tests with coverage
1128
+
1129
+ - uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
1130
+ name: Upload results to Codecov
1131
+ with:
1132
+ token: \${{ secrets.CODECOV_TOKEN }}
1133
+ `,
1134
+ "sync-github": `\
1135
+ name: Sync GitHub Templates
1136
+
1137
+ # Builds the holocron CLI from source and pushes updated workflow templates
1138
+ # and composite actions to downstream .github repos. Runs whenever the
1139
+ # template source files change on main or alpha.
1140
+ #
1141
+ # Secrets required:
1142
+ # SYNC_TOKEN — fine-grained PAT or GitHub App token with Contents write
1143
+ # access to the primary and secondary repos.
1144
+
1145
+ on: # yamllint disable-line rule:truthy
1146
+ workflow_call:
1147
+ inputs:
1148
+ primary-repo:
1149
+ description: >
1150
+ Primary .github repo — receives composite actions, reusable workflows,
1151
+ and thin-caller templates. Requires a PR (branch protection assumed).
1152
+ type: string
1153
+ required: false
1154
+ default: theholocron/.github
1155
+ secondary-repos:
1156
+ description: >
1157
+ Space-separated list of secondary repos (reusable workflows + thin
1158
+ callers only, no composite actions). Pushed directly to main.
1159
+ type: string
1160
+ required: false
1161
+ default: ""
1162
+ sync-branch:
1163
+ description: Branch name used for the primary-repo PR
1164
+ type: string
1165
+ required: false
1166
+ default: chore/sync-templates
1167
+ secrets:
1168
+ SYNC_TOKEN:
1169
+ required: true
1170
+
1171
+ jobs:
1172
+ sync:
1173
+ name: Sync templates
1174
+ runs-on: ubuntu-latest
1175
+ timeout-minutes: 15
1176
+ permissions:
1177
+ contents: read
1178
+ steps:
1179
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
1180
+ name: Checkout repository
1181
+
1182
+ - uses: theholocron/.github/.github/actions/setup@main
1183
+ name: Setup
1184
+
1185
+ - run: pnpm build
1186
+ name: Build CLI
1187
+
1188
+ - name: Sync primary repo (PR)
1189
+ run: >
1190
+ node packages/cli/dist/cli.mjs sync-github
1191
+ --repo \${{ inputs.primary-repo }}
1192
+ --branch \${{ inputs.sync-branch }}
1193
+ --pr
1194
+ env:
1195
+ GITHUB_TOKEN: \${{ secrets.SYNC_TOKEN }}
1196
+
1197
+ - name: Sync secondary repos (direct push)
1198
+ if: \${{ inputs.secondary-repos != '' }}
1199
+ run: |
1200
+ for repo in \${{ inputs.secondary-repos }}; do
1201
+ node packages/cli/dist/cli.mjs sync-github --repo "$repo"
1202
+ done
1203
+ env:
1204
+ GITHUB_TOKEN: \${{ secrets.SYNC_TOKEN }}
1205
+ `,
1206
+ typecheck: `\
1207
+ name: Typecheck
1208
+
1209
+ on: # yamllint disable-line rule:truthy
1210
+ workflow_call:
1211
+
1212
+ jobs:
1213
+ typecheck:
1214
+ name: tsc --noEmit
1215
+ permissions:
1216
+ contents: read
1217
+ runs-on: ubuntu-latest
1218
+ timeout-minutes: 10
1219
+ steps:
1220
+ - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
1221
+ name: Checkout repository
1222
+
1223
+ - uses: theholocron/.github/.github/actions/setup@main
1224
+ name: Setup
1225
+
1226
+ - run: pnpm typecheck
1227
+ name: Type check
1228
+ `
1229
+ };
1230
+ //#endregion
1231
+ //#region src/commands/setup-workflows.ts
1232
+ /**
1233
+ * Thin workflow wrapper templates for `holocron setup`.
1234
+ *
1235
+ * Each entry is a complete `.github/workflows/<name>.yml` that delegates
1236
+ * to the corresponding reusable `ci-<name>.yml` in `theholocron/.github`.
1237
+ * Files are overwritten on each setup run — they are generated artifacts.
1238
+ */
1239
+ const WORKFLOW_REPO = "theholocron/.github";
1240
+ const WORKFLOW_REF = "main";
1241
+ function ref(name) {
1242
+ return `${WORKFLOW_REPO}/.github/workflows/${name}.yml@${WORKFLOW_REF}`;
1243
+ }
1244
+ /** Header prepended when holocron setup writes a thin caller to a repo. */
1245
+ const WORKFLOW_HEADER = `\
1246
+ # AUTO-GENERATED by holocron — do not edit directly.
1247
+ # Source: theholocron/holocron · packages/cli/src/commands/setup-workflows.ts
1248
+ # Run \`holocron setup\` to regenerate.
1249
+
1250
+ `;
1251
+ const WORKFLOW_TEMPLATES = {
1252
+ lint: `\
1253
+ name: Lint
1254
+
1255
+ on: # yamllint disable-line rule:truthy
1256
+ push:
1257
+ branches: [main, alpha]
1258
+ pull_request:
1259
+
1260
+ concurrency:
1261
+ group: lint-\${{ github.ref }}
1262
+ cancel-in-progress: true
1263
+
1264
+ permissions:
1265
+ contents: write
1266
+ statuses: write
1267
+
1268
+ jobs:
1269
+ lint:
1270
+ name: Lint
1271
+ uses: ${ref("lint")}
1272
+ secrets: inherit
1273
+ with:
1274
+ enable-auto-commit: true
1275
+ `,
1276
+ test: `\
1277
+ name: Test
1278
+
1279
+ on: # yamllint disable-line rule:truthy
1280
+ push:
1281
+ branches: [main, alpha]
1282
+ pull_request:
1283
+
1284
+ concurrency:
1285
+ group: test-\${{ github.ref }}
1286
+ cancel-in-progress: true
1287
+
1288
+ permissions:
1289
+ contents: read
1290
+
1291
+ jobs:
1292
+ test:
1293
+ name: Test
1294
+ uses: ${ref("test")}
1295
+ secrets: inherit
1296
+ `,
1297
+ typecheck: `\
1298
+ name: Typecheck
1299
+
1300
+ on: # yamllint disable-line rule:truthy
1301
+ push:
1302
+ branches: [main, alpha]
1303
+ pull_request:
1304
+
1305
+ concurrency:
1306
+ group: typecheck-\${{ github.ref }}
1307
+ cancel-in-progress: true
1308
+
1309
+ permissions:
1310
+ contents: read
1311
+
1312
+ jobs:
1313
+ typecheck:
1314
+ name: Typecheck
1315
+ uses: ${ref("typecheck")}
1316
+ secrets: inherit
1317
+ `,
1318
+ codeql: `\
1319
+ name: CodeQL
1320
+
1321
+ on: # yamllint disable-line rule:truthy
1322
+ push:
1323
+ branches:
1324
+ - main
1325
+ pull_request:
1326
+ branches:
1327
+ - main
1328
+ schedule:
1329
+ - cron: "0 0 * * 1"
1330
+
1331
+ permissions:
1332
+ actions: read
1333
+ contents: read
1334
+ security-events: write
1335
+
1336
+ jobs:
1337
+ codeql:
1338
+ uses: ${ref("codeql")}
1339
+ secrets: inherit
1340
+ `,
1341
+ review: `\
1342
+ name: Review
1343
+
1344
+ on: # yamllint disable-line rule:truthy
1345
+ pull_request:
1346
+
1347
+ concurrency:
1348
+ group: review-\${{ github.ref }}
1349
+ cancel-in-progress: true
1350
+
1351
+ permissions:
1352
+ contents: read
1353
+ checks: write
1354
+ pull-requests: write
1355
+
1356
+ jobs:
1357
+ review:
1358
+ name: Review
1359
+ uses: ${ref("review")}
1360
+ secrets: inherit
1361
+ `,
1362
+ release: `\
1363
+ name: Release
1364
+
1365
+ on: # yamllint disable-line rule:truthy
1366
+ push:
1367
+ branches:
1368
+ - main
1369
+ - alpha
1370
+ workflow_dispatch:
1371
+
1372
+ permissions:
1373
+ contents: write
1374
+ id-token: write
1375
+ issues: write
1376
+ pull-requests: write
1377
+
1378
+ concurrency:
1379
+ group: \${{ github.workflow }}-\${{ github.ref }}
1380
+ cancel-in-progress: false
1381
+
1382
+ jobs:
1383
+ release:
1384
+ uses: ${ref("release")}
1385
+ secrets: inherit
1386
+ `,
1387
+ stale: `\
1388
+ name: Stale
1389
+
1390
+ on: # yamllint disable-line rule:truthy
1391
+ schedule:
1392
+ - cron: "30 1 * * *"
1393
+
1394
+ permissions:
1395
+ contents: write
1396
+ issues: write
1397
+ pull-requests: write
1398
+
1399
+ jobs:
1400
+ stale:
1401
+ uses: ${ref("stale")}
1402
+ secrets: inherit
1403
+ `,
1404
+ greetings: `\
1405
+ name: Greetings
1406
+
1407
+ on: # yamllint disable-line rule:truthy
1408
+ pull_request:
1409
+ issues:
1410
+
1411
+ permissions:
1412
+ issues: write
1413
+ pull-requests: write
1414
+
1415
+ jobs:
1416
+ greetings:
1417
+ uses: ${ref("greetings")}
1418
+ secrets: inherit
1419
+ `,
1420
+ dependencies: `\
1421
+ name: Dependencies
1422
+
1423
+ on: # yamllint disable-line rule:truthy
1424
+ pull_request:
1425
+
1426
+ permissions:
1427
+ contents: write
1428
+ pull-requests: write
1429
+
1430
+ jobs:
1431
+ dependencies:
1432
+ uses: ${ref("dependencies")}
1433
+ secrets: inherit
1434
+ `,
1435
+ "bookkeeping-pr": `\
1436
+ name: PR Bookkeeping
1437
+
1438
+ on: # yamllint disable-line rule:truthy
1439
+ pull_request:
1440
+ types:
1441
+ - opened
1442
+ - edited
1443
+
1444
+ permissions:
1445
+ contents: read
1446
+ pull-requests: write
1447
+
1448
+ jobs:
1449
+ bookkeeping:
1450
+ uses: ${ref("bookkeeping-pr")}
1451
+ secrets: inherit
1452
+ `,
1453
+ audit: `\
1454
+ name: Audit
1455
+
1456
+ on: # yamllint disable-line rule:truthy
1457
+ push:
1458
+ branches: [main, alpha]
1459
+ pull_request:
1460
+
1461
+ permissions:
1462
+ contents: read
1463
+
1464
+ jobs:
1465
+ audit:
1466
+ uses: ${ref("audit")}
1467
+ secrets: inherit
1468
+ `,
1469
+ "sync-github": `\
1470
+ name: Sync GitHub Templates
1471
+
1472
+ on: # yamllint disable-line rule:truthy
1473
+ push:
1474
+ branches: [main, alpha]
1475
+ paths:
1476
+ - packages/cli/src/templates/index.ts
1477
+ - packages/cli/src/commands/setup-workflows.ts
1478
+
1479
+ concurrency:
1480
+ group: sync-github-\${{ github.ref }}
1481
+ cancel-in-progress: true
1482
+
1483
+ permissions:
1484
+ contents: read
1485
+
1486
+ jobs:
1487
+ sync:
1488
+ name: Sync
1489
+ uses: ${ref("sync-github")}
1490
+ with:
1491
+ secondary-repos: theholocron/.github-private
1492
+ secrets:
1493
+ SYNC_TOKEN: \${{ secrets.SYNC_TOKEN }}
1494
+ `
1495
+ };
1496
+ const KNOWN_WORKFLOWS = new Set(Object.keys(WORKFLOW_TEMPLATES));
1497
+ //#endregion
1498
+ //#region src/commands/sync-github.ts
1499
+ const DEFAULT_REPO = "theholocron/.github";
1500
+ const API_BASE = "https://api.github.com";
1501
+ function reusableHeader(source) {
1502
+ return [
1503
+ `# AUTO-GENERATED — do not edit in theholocron/.github directly.`,
1504
+ `# Source: theholocron/holocron · ${source}`,
1505
+ `# Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
1506
+ `# Tool: holocron sync-github`,
1507
+ `# Changes: edit source in theholocron/holocron and push to alpha or main.`,
1508
+ ``
1509
+ ].join("\n");
1510
+ }
1511
+ function thinCallerHeader() {
1512
+ return [
1513
+ `# AUTO-GENERATED — do not edit in theholocron/.github directly.`,
1514
+ `# Source: theholocron/holocron · packages/cli/src/commands/setup-workflows.ts`,
1515
+ `# Synced: ${(/* @__PURE__ */ new Date()).toISOString()}`,
1516
+ `# Tool: holocron sync-github`,
1517
+ `# Changes: edit setup-workflows.ts in theholocron/holocron and push.`,
1518
+ ``
1519
+ ].join("\n");
1520
+ }
1521
+ function buildBatch(repo) {
1522
+ const files = [];
1523
+ if (repo === DEFAULT_REPO) for (const [name, content] of Object.entries(ACTIONS)) files.push({
1524
+ path: `.github/actions/${name}.yml`,
1525
+ content: reusableHeader(`packages/cli/src/templates/index.ts`) + content
1526
+ });
1527
+ for (const [name, content] of Object.entries(REUSABLE_WORKFLOWS)) files.push({
1528
+ path: `.github/workflows/${name}.yml`,
1529
+ content: reusableHeader(`packages/cli/src/templates/index.ts`) + content
1530
+ });
1531
+ for (const [name, content] of Object.entries(WORKFLOW_TEMPLATES)) files.push({
1532
+ path: `workflow-templates/${name}.yml`,
1533
+ content: thinCallerHeader() + content
1534
+ });
1535
+ return files;
1536
+ }
1537
+ async function runSyncGithub(input) {
1538
+ const print = input.print ?? ((line) => console.log(line));
1539
+ const repo = input.repo ?? DEFAULT_REPO;
1540
+ const [owner, repoName] = repo.split("/");
1541
+ const { token, dryRun = false, branch, createPr = false } = input;
1542
+ const message = input.message ?? `chore: sync from theholocron/holocron`;
1543
+ const fetchFn = input.fetch ?? globalThis.fetch;
1544
+ const headers = {
1545
+ Authorization: `Bearer ${token}`,
1546
+ Accept: "application/vnd.github+json",
1547
+ "Content-Type": "application/json",
1548
+ "X-GitHub-Api-Version": "2022-11-28"
1549
+ };
1550
+ print(`holocron sync-github${dryRun ? " (dry-run)" : ""}`);
1551
+ print(` repo: ${repo}`);
1552
+ if (branch) print(` branch: ${branch}`);
1553
+ print("");
1554
+ const batch = buildBatch(repo);
1555
+ let created = 0;
1556
+ let updated = 0;
1557
+ let unchanged = 0;
1558
+ for (const file of batch) {
1559
+ const ref = branch ? `?ref=${encodeURIComponent(branch)}` : "";
1560
+ const url = `${API_BASE}/repos/${owner}/${repoName}/contents/${file.path}`;
1561
+ const newContent = Buffer.from(file.content, "utf8").toString("base64");
1562
+ let existingSha;
1563
+ let existingContent;
1564
+ try {
1565
+ const getRes = await fetchFn(`${url}${ref}`, { headers });
1566
+ if (getRes.ok) {
1567
+ const data = await getRes.json();
1568
+ existingSha = data.sha;
1569
+ existingContent = data.content.replace(/\n/g, "");
1570
+ }
1571
+ } catch {}
1572
+ if (existingContent === newContent) {
1573
+ print(` · unchanged ${file.path}`);
1574
+ unchanged++;
1575
+ continue;
1576
+ }
1577
+ const verb = existingSha ? "updated " : "created ";
1578
+ if (dryRun) {
1579
+ print(` ~ ${verb} ${file.path}`);
1580
+ if (existingSha) updated++;
1581
+ else created++;
1582
+ continue;
1583
+ }
1584
+ const body = {
1585
+ message,
1586
+ content: newContent
1587
+ };
1588
+ if (existingSha) body.sha = existingSha;
1589
+ if (branch) body.branch = branch;
1590
+ const putRes = await fetchFn(url, {
1591
+ method: "PUT",
1592
+ headers,
1593
+ body: JSON.stringify(body)
1594
+ });
1595
+ if (!putRes.ok) {
1596
+ const err = await putRes.json();
1597
+ const msg = `failed to push ${file.path}: ${err.message ?? putRes.status}`;
1598
+ print(` ✗ ${msg}`);
1599
+ return {
1600
+ status: "fail",
1601
+ created,
1602
+ updated,
1603
+ unchanged,
1604
+ message: msg
1605
+ };
1606
+ }
1607
+ print(` ✓ ${verb} ${file.path}`);
1608
+ if (existingSha) updated++;
1609
+ else created++;
1610
+ }
1611
+ const changed = created + updated;
1612
+ print("");
1613
+ print(` ${created} created, ${updated} updated, ${unchanged} unchanged`);
1614
+ let prUrl;
1615
+ if (branch && createPr && changed > 0 && !dryRun) {
1616
+ const prRes = await fetchFn(`${API_BASE}/repos/${owner}/${repoName}/pulls`, {
1617
+ method: "POST",
1618
+ headers,
1619
+ body: JSON.stringify({
1620
+ title: message,
1621
+ head: branch,
1622
+ base: "main",
1623
+ body: "Auto-generated by `holocron sync-github`. Review and merge to apply template updates."
1624
+ })
1625
+ });
1626
+ if (prRes.ok) {
1627
+ prUrl = (await prRes.json()).html_url;
1628
+ print(` → PR opened: ${prUrl}`);
1629
+ } else {
1630
+ const err = await prRes.json();
1631
+ if (err.errors?.some((e) => e.message.includes("already exists"))) print(` → PR already open for ${branch} — branch updated, ready to merge`);
1632
+ else print(` ⚠ PR creation failed: ${err.message ?? prRes.status}`);
1633
+ }
1634
+ }
1635
+ return {
1636
+ status: dryRun ? "dry-run" : "ok",
1637
+ created,
1638
+ updated,
1639
+ unchanged,
1640
+ prUrl
1641
+ };
1642
+ }
1643
+ //#endregion
449
1644
  //#region src/commands/npm-publish-initial.ts
450
1645
  /**
451
1646
  * `holocron npm publish-initial` — bottles up the chicken-and-egg
@@ -1865,241 +3060,6 @@ function vaultProviderName(loader) {
1865
3060
  return loader.get("vault").providerName;
1866
3061
  }
1867
3062
  //#endregion
1868
- //#region src/commands/setup-workflows.ts
1869
- /**
1870
- * Thin workflow wrapper templates for `holocron setup`.
1871
- *
1872
- * Each entry is a complete `.github/workflows/<name>.yml` that delegates
1873
- * to the corresponding reusable `ci-<name>.yml` in `theholocron/.github`.
1874
- * Files are overwritten on each setup run — they are generated artifacts.
1875
- */
1876
- const WORKFLOW_REPO = "theholocron/.github";
1877
- const WORKFLOW_REF = "main";
1878
- function ref(name) {
1879
- return `${WORKFLOW_REPO}/.github/workflows/${name}.yml@${WORKFLOW_REF}`;
1880
- }
1881
- /** Header prepended when holocron setup writes a thin caller to a repo. */
1882
- const WORKFLOW_HEADER = `\
1883
- # AUTO-GENERATED by holocron — do not edit directly.
1884
- # Source: theholocron/holocron · packages/cli/src/commands/setup-workflows.ts
1885
- # Run \`holocron setup\` to regenerate.
1886
-
1887
- `;
1888
- const WORKFLOW_TEMPLATES = {
1889
- lint: `\
1890
- name: Lint
1891
-
1892
- on: # yamllint disable-line rule:truthy
1893
- push:
1894
- branches: [main, alpha]
1895
- pull_request:
1896
-
1897
- concurrency:
1898
- group: lint-\${{ github.ref }}
1899
- cancel-in-progress: true
1900
-
1901
- permissions:
1902
- contents: write
1903
- statuses: write
1904
-
1905
- jobs:
1906
- lint:
1907
- name: Lint
1908
- uses: ${ref("lint")}
1909
- secrets: inherit
1910
- with:
1911
- enable-auto-commit: true
1912
- `,
1913
- test: `\
1914
- name: Test
1915
-
1916
- on: # yamllint disable-line rule:truthy
1917
- push:
1918
- branches: [main, alpha]
1919
- pull_request:
1920
-
1921
- concurrency:
1922
- group: test-\${{ github.ref }}
1923
- cancel-in-progress: true
1924
-
1925
- permissions:
1926
- contents: read
1927
-
1928
- jobs:
1929
- test:
1930
- name: Test
1931
- uses: ${ref("test")}
1932
- secrets: inherit
1933
- `,
1934
- typecheck: `\
1935
- name: Typecheck
1936
-
1937
- on: # yamllint disable-line rule:truthy
1938
- push:
1939
- branches: [main, alpha]
1940
- pull_request:
1941
-
1942
- concurrency:
1943
- group: typecheck-\${{ github.ref }}
1944
- cancel-in-progress: true
1945
-
1946
- permissions:
1947
- contents: read
1948
-
1949
- jobs:
1950
- typecheck:
1951
- name: Typecheck
1952
- uses: ${ref("typecheck")}
1953
- secrets: inherit
1954
- `,
1955
- codeql: `\
1956
- name: CodeQL
1957
-
1958
- on: # yamllint disable-line rule:truthy
1959
- push:
1960
- branches:
1961
- - main
1962
- pull_request:
1963
- branches:
1964
- - main
1965
- schedule:
1966
- - cron: "0 0 * * 1"
1967
-
1968
- permissions:
1969
- actions: read
1970
- contents: read
1971
- security-events: write
1972
-
1973
- jobs:
1974
- codeql:
1975
- uses: ${ref("codeql")}
1976
- secrets: inherit
1977
- `,
1978
- review: `\
1979
- name: Review
1980
-
1981
- on: # yamllint disable-line rule:truthy
1982
- pull_request:
1983
-
1984
- concurrency:
1985
- group: review-\${{ github.ref }}
1986
- cancel-in-progress: true
1987
-
1988
- permissions:
1989
- contents: read
1990
- checks: write
1991
- pull-requests: write
1992
-
1993
- jobs:
1994
- review:
1995
- name: Review
1996
- uses: ${ref("review")}
1997
- secrets: inherit
1998
- `,
1999
- release: `\
2000
- name: Release
2001
-
2002
- on: # yamllint disable-line rule:truthy
2003
- push:
2004
- branches:
2005
- - main
2006
-
2007
- permissions:
2008
- contents: write
2009
- id-token: write
2010
- issues: write
2011
- pull-requests: write
2012
-
2013
- jobs:
2014
- release:
2015
- uses: ${ref("release")}
2016
- secrets: inherit
2017
- `,
2018
- stale: `\
2019
- name: Stale
2020
-
2021
- on: # yamllint disable-line rule:truthy
2022
- schedule:
2023
- - cron: "30 1 * * *"
2024
-
2025
- permissions:
2026
- contents: write
2027
- issues: write
2028
- pull-requests: write
2029
-
2030
- jobs:
2031
- stale:
2032
- uses: ${ref("stale")}
2033
- secrets: inherit
2034
- `,
2035
- greetings: `\
2036
- name: Greetings
2037
-
2038
- on: # yamllint disable-line rule:truthy
2039
- pull_request:
2040
- issues:
2041
-
2042
- permissions:
2043
- issues: write
2044
- pull-requests: write
2045
-
2046
- jobs:
2047
- greetings:
2048
- uses: ${ref("greetings")}
2049
- secrets: inherit
2050
- `,
2051
- dependencies: `\
2052
- name: Dependencies
2053
-
2054
- on: # yamllint disable-line rule:truthy
2055
- pull_request:
2056
-
2057
- permissions:
2058
- contents: write
2059
- pull-requests: write
2060
-
2061
- jobs:
2062
- dependencies:
2063
- uses: ${ref("dependencies")}
2064
- secrets: inherit
2065
- `,
2066
- "bookkeeping-pr": `\
2067
- name: PR Bookkeeping
2068
-
2069
- on: # yamllint disable-line rule:truthy
2070
- pull_request:
2071
- types:
2072
- - opened
2073
- - edited
2074
-
2075
- permissions:
2076
- contents: read
2077
- pull-requests: write
2078
-
2079
- jobs:
2080
- bookkeeping:
2081
- uses: ${ref("bookkeeping-pr")}
2082
- secrets: inherit
2083
- `,
2084
- audit: `\
2085
- name: Audit
2086
-
2087
- on: # yamllint disable-line rule:truthy
2088
- push:
2089
- branches: [main, alpha]
2090
- pull_request:
2091
-
2092
- permissions:
2093
- contents: read
2094
-
2095
- jobs:
2096
- audit:
2097
- uses: ${ref("audit")}
2098
- secrets: inherit
2099
- `
2100
- };
2101
- const KNOWN_WORKFLOWS = new Set(Object.keys(WORKFLOW_TEMPLATES));
2102
- //#endregion
2103
3063
  //#region src/commands/setup.ts
2104
3064
  const DEPENDABOT_CONFIG = `\
2105
3065
  # AUTO-GENERATED by holocron — run \`holocron setup\` to regenerate.
@@ -2653,7 +3613,36 @@ await yargs(hideBin(process.argv)).scriptName("holocron").usage("$0 <command> [o
2653
3613
  dryRun: argv.dryRun,
2654
3614
  ...argv.otp ? { otp: argv.otp } : {}
2655
3615
  })).status === "fail") process.exitCode = 1;
2656
- }).demandCommand(1, "Run `holocron npm --help` to see available npm subcommands."), () => {}).command("config show", "Print the resolved holocron config", () => {}, async (argv) => {
3616
+ }).demandCommand(1, "Run `holocron npm --help` to see available npm subcommands."), () => {}).command("sync-github", "Sync workflow templates and composite actions to theholocron/.github via the GitHub API", (y) => y.option("repo", {
3617
+ type: "string",
3618
+ default: "theholocron/.github",
3619
+ describe: "Target org/repo (default: theholocron/.github)"
3620
+ }).option("branch", {
3621
+ type: "string",
3622
+ describe: "Push to this branch instead of the default branch (enables PR-based workflow for protected repos)"
3623
+ }).option("pr", {
3624
+ type: "boolean",
3625
+ default: false,
3626
+ describe: "Open a PR after pushing to --branch (no-op without --branch)"
3627
+ }).option("message", {
3628
+ type: "string",
3629
+ describe: "Commit message (default: chore: sync from theholocron/holocron)"
3630
+ }), async (argv) => {
3631
+ const token = argv.token ?? process.env.GITHUB_TOKEN ?? process.env.HOLOCRON_GITHUB_TOKEN;
3632
+ if (!token) {
3633
+ console.error("sync-github: GitHub token required — pass --token or set GITHUB_TOKEN");
3634
+ process.exitCode = 1;
3635
+ return;
3636
+ }
3637
+ if ((await runSyncGithub({
3638
+ token,
3639
+ repo: argv.repo,
3640
+ dryRun: argv.dryRun,
3641
+ ...argv.branch ? { branch: argv.branch } : {},
3642
+ ...argv.pr ? { createPr: true } : {},
3643
+ ...argv.message ? { message: argv.message } : {}
3644
+ })).status === "fail") process.exitCode = 1;
3645
+ }).command("config show", "Print the resolved holocron config", () => {}, async (argv) => {
2657
3646
  const loaded = await loadConfig(argv.cwd);
2658
3647
  console.log(JSON.stringify(loaded.resolved, null, 2));
2659
3648
  }).command("plugin create <slug> <vendor>", "Scaffold a new @theholocron/holocron-plugin-<slug> package", (y) => y.positional("slug", {