11import { existsSync } from "node:fs" ;
2- import { mkdir , readFile , writeFile } from "node:fs/promises" ;
3- import { dirname , join } from "node:path" ;
4- import { fileURLToPath } from "node:url" ;
2+ import { mkdir , writeFile } from "node:fs/promises" ;
3+ import { dirname } from "node:path" ;
54import { args , color , confirm , Exit , log , multiselect , note } from "@r5n/cli-core" ;
65import { BaseCommand , type Ctx } from "../../base-command" ;
6+ import { detectProvider } from "../../providers" ;
7+ import { createCiGenerator , GitLabCiGenerator , WORKFLOW_CONFIGS , type WorkflowType } from "./CiGenerator" ;
78
8- const WORKFLOWS_DIR = ".github/workflows" ;
9- const TEMPLATES_DIR = join ( dirname ( fileURLToPath ( import . meta. url ) ) , "workflows" ) ;
10-
11- type WorkflowType = "create-stone" | "release-pr" | "release" ;
12-
13- type WorkflowConfig = { name : string ; description : string ; filename : string } ;
14-
15- const WORKFLOWS : Record < WorkflowType , WorkflowConfig > = {
16- "create-stone" : {
17- description : "When a PR is merged, automatically create a stone" ,
18- filename : "sis-create-stone.yml" ,
19- name : "Auto-create stone from PR" ,
20- } ,
21- release : {
22- description : "When the release PR is merged, run the release" ,
23- filename : "sis-release.yml" ,
24- name : "Auto-release on PR merge" ,
25- } ,
26- "release-pr" : {
27- description : "When stones are added, create or update a release PR" ,
28- filename : "sis-release-pr.yml" ,
29- name : "Create/update release PR" ,
30- } ,
31- } ;
9+ const PROVIDER_LABELS = { bitbucket : "Bitbucket Pipelines" , github : "GitHub Actions" , gitlab : "GitLab CI" } ;
3210
3311const initArgs = args ( {
3412 all : { alias : "a" , default : false , description : "Install all workflows" , type : "boolean" } ,
@@ -43,55 +21,74 @@ type InitCtx = Ctx<typeof initArgs>;
4321
4422export class ActionsInitCommand extends BaseCommand {
4523 name = "init" ;
46- description = "Set up GitHub Actions workflows" ;
24+ description = "Set up CI workflows for automated releases " ;
4725 args = initArgs ;
4826 prompts = true ;
4927
5028 async execute ( ctx : InitCtx ) {
29+ const provider = await detectProvider ( ) ;
30+ const generator = createCiGenerator ( provider ) ;
31+
32+ log . info ( `Detected ${ color . bold ( PROVIDER_LABELS [ provider ] ) } repository\n` ) ;
33+
5134 const selected = await this . selectWorkflows ( ctx ) ;
5235
5336 if ( selected . length === 0 ) {
5437 log . info ( color . dim ( "No workflows selected" ) ) ;
5538 return ;
5639 }
5740
58- const existing = this . findExistingWorkflows ( selected ) ;
41+ const existing = generator . getExistingFiles ( selected ) ;
5942 if ( existing . length > 0 && ! ctx . args . yes ) {
6043 if ( ! ctx . interactive ) {
6144 throw new Exit ( `Workflows already exist: ${ existing . join ( ", " ) } ` , "Use --yes to overwrite existing workflows" ) ;
6245 }
63- log . warn ( `The following workflows already exist:\n${ existing . map ( ( f ) => ` ${ f } ` ) . join ( "\n" ) } ` ) ;
64- const overwrite = await confirm ( { initialValue : false , message : "Overwrite existing workflows ?" } ) ;
46+ log . warn ( `The following files already exist:\n${ existing . map ( ( f ) => ` ${ f } ` ) . join ( "\n" ) } ` ) ;
47+ const overwrite = await confirm ( { initialValue : false , message : "Overwrite existing files ?" } ) ;
6548 if ( ! overwrite ) return ;
6649 }
6750
51+ const files = await generator . generate ( selected ) ;
52+
6853 if ( ctx . args . dryRun ) {
69- await this . previewWorkflows ( selected ) ;
54+ for ( const file of files ) {
55+ log . info ( `\n${ color . bold ( file . path ) } :` ) ;
56+ log . info ( color . dim ( file . content ) ) ;
57+ }
7058 return ;
7159 }
7260
73- await this . createWorkflows ( selected ) ;
61+ for ( const file of files ) {
62+ const dir = dirname ( file . path ) ;
63+ if ( dir !== "." && ! existsSync ( dir ) ) {
64+ await mkdir ( dir , { recursive : true } ) ;
65+ }
66+ await writeFile ( file . path , file . content , "utf-8" ) ;
67+ }
68+
69+ note ( files . map ( ( f ) => ` ${ color . green ( "+" ) } ${ f . path } ` ) . join ( "\n" ) , color . green ( "CI workflows created" ) ) ;
7470
75- note (
76- selected . map ( ( w ) => ` ${ color . green ( "+" ) } ${ WORKFLOWS [ w ] . filename } ` ) . join ( "\n" ) ,
77- color . green ( "Workflows created" ) ,
78- ) ;
71+ if ( generator instanceof GitLabCiGenerator ) {
72+ log . info ( `\nAdd this to your ${ color . bold ( ".gitlab-ci.yml" ) } :\n` ) ;
73+ log . info ( color . cyan ( generator . getIncludeInstruction ( ) ) ) ;
74+ log . info ( "" ) ;
75+ }
7976
80- log . info ( color . dim ( "\nCommit and push these files to enable the workflows." ) ) ;
77+ log . info ( color . dim ( "Commit and push these files to enable the workflows." ) ) ;
8178 }
8279
8380 private async selectWorkflows ( ctx : InitCtx ) : Promise < WorkflowType [ ] > {
8481 const fromFlags = this . getWorkflowsFromFlags ( ctx ) ;
8582 if ( fromFlags . length > 0 ) return fromFlags ;
8683
8784 if ( ctx . args . all || ! ctx . interactive ) {
88- return Object . keys ( WORKFLOWS ) as WorkflowType [ ] ;
85+ return Object . keys ( WORKFLOW_CONFIGS ) as WorkflowType [ ] ;
8986 }
9087
9188 return multiselect ( {
92- initialValues : Object . keys ( WORKFLOWS ) as WorkflowType [ ] ,
89+ initialValues : Object . keys ( WORKFLOW_CONFIGS ) as WorkflowType [ ] ,
9390 message : "Select workflows to create" ,
94- options : Object . entries ( WORKFLOWS ) . map ( ( [ key , config ] ) => ( {
91+ options : Object . entries ( WORKFLOW_CONFIGS ) . map ( ( [ key , config ] ) => ( {
9592 hint : config . description ,
9693 label : config . name ,
9794 value : key as WorkflowType ,
@@ -107,34 +104,4 @@ export class ActionsInitCommand extends BaseCommand {
107104 if ( ctx . args . release ) selected . push ( "release" ) ;
108105 return selected ;
109106 }
110-
111- private findExistingWorkflows ( selected : WorkflowType [ ] ) : string [ ] {
112- return selected . map ( ( w ) => WORKFLOWS [ w ] . filename ) . filter ( ( filename ) => existsSync ( join ( WORKFLOWS_DIR , filename ) ) ) ;
113- }
114-
115- private async readTemplate ( filename : string ) : Promise < string > {
116- return readFile ( join ( TEMPLATES_DIR , filename ) , "utf-8" ) ;
117- }
118-
119- private async previewWorkflows ( selected : WorkflowType [ ] ) {
120- for ( const workflow of selected ) {
121- const config = WORKFLOWS [ workflow ] ;
122- const content = await this . readTemplate ( config . filename ) ;
123- log . info ( `\n${ color . bold ( config . filename ) } :` ) ;
124- log . info ( color . dim ( content ) ) ;
125- }
126- }
127-
128- private async createWorkflows ( selected : WorkflowType [ ] ) {
129- if ( ! existsSync ( WORKFLOWS_DIR ) ) {
130- await mkdir ( WORKFLOWS_DIR , { recursive : true } ) ;
131- }
132-
133- for ( const workflow of selected ) {
134- const config = WORKFLOWS [ workflow ] ;
135- const content = await this . readTemplate ( config . filename ) ;
136- const filepath = join ( WORKFLOWS_DIR , config . filename ) ;
137- await writeFile ( filepath , content , "utf-8" ) ;
138- }
139- }
140107}
0 commit comments