import { VariableGroup } from "azure-devops-node-api/interfaces/ReleaseInterfaces" ; import commander from "commander" ; import path from "path" ; import { echo } from "shelljs" ; import { Bedrock , Config , readYaml , write } from "../../config" ; import * as bedrockYaml from "../../lib/bedrockYaml" ; import { build as buildCmd , exit as exitCmd , populateInheritValueFromConfig , validateForRequiredValues , } from "../../lib/commandBuilder" ; import { PROJECT_PIPELINE_FILENAME } from "../../lib/constants" ; import { addVariableGroup } from "../../lib/pipelines/variableGroup" ; import { hasValue , validateProjectNameThrowable , validateOrgNameThrowable , } from "../../lib/validator" ; import { logger } from "../../logger" ; import { AzurePipelinesYaml , BedrockFileInfo , VariableGroupData , VariableGroupDataVariable , } from "../../types" ; import decorator from "./create-variable-group.decorator.json" ; import { build as buildError , log as logError } from "../../lib/errorBuilder" ; import { errorStatusCode } from "../../lib/errorStatusCode" ; export interface CommandOptions { registryName : string | undefined ; servicePrincipalId : string | undefined ; servicePrincipalPassword : string | undefined ; tenant : string | undefined ; hldRepoUrl : string | undefined ; orgName : string | undefined ; personalAccessToken : string | undefined ; devopsProject : string | undefined ; } interface ConfigValues { hldRepoUrl : string ; orgName : string ; personalAccessToken : string ; devopsProject : string ; registryName : string ; servicePrincipalId : string ; servicePrincipalPassword : string ; tenant : string ; } export const checkDependencies = ( projectPath ) : void => { const fileInfo = bedrockYaml . fileInfo ( projectPath ) ; if ( fileInfo . exist === false ) { throw buildError ( errorStatusCode . VALIDATION_ERR , "project-create-variable-group-cmd-err-bedrock-yaml-missing" ) ; } } ; param variableGroupName param registryName param hldRepoUrl param servicePrincipalId param servicePrincipalPassword param tenantId param accessOpts export const create = ( variableGroupName , values ) : Promise < VariableGroup > => { logger . info ( `Creating Variable Group from group definition '${ variableGroupName }'` ) ; const vars = { ACR_NAME : { value : values . registryName , } , HLD_REPO : { value : values . hldRepoUrl , } , PAT : { isSecret : true , value : values . personalAccessToken , } , SP_APP_ID : { isSecret : true , value : values . servicePrincipalId , } , SP_PASS : { isSecret : true , value : values . servicePrincipalPassword , } , SP_TENANT : { isSecret : true , value : values . tenant , } , } ; const variableGroupData = { description : "Created from bedrock CLI" , name : variableGroupName , type : "Vsts" , variables : vars , } ; return addVariableGroup ( variableGroupData , { orgName : values . orgName , personalAccessToken : values . personalAccessToken , project : values . devopsProject , } ) ; } ; param rootProjectPath param variableGroupName export const setVariableGroupInBedrockFile = ( rootProjectPath , variableGroupName ) : void => { if ( ! hasValue ( rootProjectPath ) ) { throw buildError ( errorStatusCode . VALIDATION_ERR , "project-create-variable-group-cmd-err-root-invalid" ) ; } if ( ! hasValue ( variableGroupName ) ) { throw buildError ( errorStatusCode . VALIDATION_ERR , "project-create-variable-group-cmd-err-variable-group-invalid" ) ; } logger . info ( `Setting variable group ${ variableGroupName }` ) ; const bedrockFile = Bedrock ( rootProjectPath ) ; bedrockYaml . addVariableGroup ( bedrockFile , rootProjectPath , variableGroupName ) ; } ; param rootProjectPath export const updateLifeCyclePipeline = ( rootProjectPath ) : void => { if ( ! hasValue ( rootProjectPath ) ) { throw buildError ( errorStatusCode . VALIDATION_ERR , "project-create-variable-group-cmd-err-root-invalid" ) ; } const fileName = PROJECT_PIPELINE_FILENAME ; const absProjectRoot = path . resolve ( rootProjectPath ) ; const bedrockFile = Bedrock ( rootProjectPath ) ; const pipelineFile = readYaml ( path . join ( absProjectRoot , fileName ) ) as AzurePipelinesYaml ; if ( ! pipelineFile ) { throw buildError ( errorStatusCode . VALIDATION_ERR , { errorKey : "project-create-variable-group-cmd-err-file-missing" , values : [ fileName , absProjectRoot ] , } ) ; } logger . verbose ( `${ fileName } content: \n ${ JSON . stringify ( pipelineFile ) }` ) ; logger . debug ( `Setting variable groups ${ JSON . stringify ( bedrockFile . variableGroups ) } in lifecycle pipeline yaml file ${ fileName }` ) ; pipelineFile . variables = [ ... ( bedrockFile . variableGroups ?? [ ] ) . map ( ( groupName ) => { return { group : groupName , } ; } ) , ] ; write ( pipelineFile , absProjectRoot , fileName ) ; } ; export const validateValues = ( opts ) : ConfigValues => { populateInheritValueFromConfig ( decorator , Config ( ) , opts ) ; validateForRequiredValues ( decorator , opts , true ) ; validateProjectNameThrowable ( opts . devopsProject || "" ) ; validateOrgNameThrowable ( opts . orgName || "" ) ; return { hldRepoUrl : opts . hldRepoUrl || "" , orgName : opts . orgName || "" , personalAccessToken : opts . personalAccessToken || "" , devopsProject : opts . devopsProject || "" , registryName : opts . registryName || "" , servicePrincipalId : opts . servicePrincipalId || "" , servicePrincipalPassword : opts . servicePrincipalPassword || "" , tenant : opts . tenant || "" , } ; } ; param variableGroupName param opts export const execute = async ( variableGroupName , opts , exitFn ) : Promise < void > => { if ( ! hasValue ( variableGroupName ) ) { await exitFn ( 1 ) ; return ; } try { const projectPath = process . cwd ( ) ; logger . verbose ( `project path: ${ projectPath }` ) ; checkDependencies ( projectPath ) ; const values = validateValues ( opts ) ; const variableGroup = await create ( variableGroupName , values ) ; setVariableGroupInBedrockFile ( projectPath , variableGroup . name || "" ) ; updateLifeCyclePipeline ( projectPath ) ; echo ( JSON . stringify ( variableGroup , null , 2 ) ) ; logger . info ( "Successfully created a variable group in Azure DevOps project!" ) ; await exitFn ( 0 ) ; } catch ( err ) { logError ( buildError ( errorStatusCode . CMD_EXE_ERR , "project-create-variable-group-cmd-failed" , err ) ) ; await exitFn ( 1 ) ; } } ; param command export const commandDecorator = ( command ) : void => { buildCmd ( command , decorator ) . action ( async ( variableGroupName , opts ) => { await execute ( variableGroupName , opts , async ( status ) => { await exitCmd ( logger , process . exit , status ) ; } ) ; } ) ; } ;