import * as fs from "fs-plus" ; import * as path from "path" ; import * as vscode from "vscode" ; import { RemoteContainersCommands , VscodeCommands } from "../common/Commands" ; import { ArgumentEmptyOrNullError } from "../common/Error/OperationFailedErrors/ArgumentEmptyOrNullError" ; import { TypeNotSupportedError } from "../common/Error/SystemErrors/TypeNotSupportedError" ; import { OperationCanceledError } from "../common/Error/OperationCanceledError" ; import { ConfigKey , EventNames , FileNames , ScaffoldType } from "../constants" ; import { FileUtility } from "../FileUtility" ; import { TelemetryContext , TelemetryWorker } from "../telemetry" ; import { getProjectConfig , updateProjectHostTypeConfig } from "../utils" ; import { Component } from "./Interfaces/Component" ; import { ProjectHostType } from "./Interfaces/ProjectHostType" ; import { ProjectTemplateType , TemplateFileInfo } from "./Interfaces/ProjectTemplate" ; import { IoTWorkbenchProjectBase , OpenScenario } from "./IoTWorkbenchProjectBase" ; import { RemoteExtension } from "./RemoteExtension" ; import { ProjectConfigNotFoundError } from "../common/Error/SystemErrors/ProjectConfigNotFoundError" ; import { DirectoryNotFoundError } from "../common/Error/OperationFailedErrors/DirectoryNotFoundError" ; const importLazy = require ( "import-lazy" ) ; const raspberryPiDeviceModule = importLazy ( ( ) => require ( "./RaspberryPiDevice" ) ) ( ) ; export class IoTContainerizedProject extends IoTWorkbenchProjectBase { constructor ( context , channel , telemetryContext , rootFolderPath ) { super ( context , channel , telemetryContext ) ; this . projectHostType = ProjectHostType . Container ; if ( ! rootFolderPath ) { throw new ArgumentEmptyOrNullError ( "root folder path" , "" , "Please initialize iot containerized project with root folder path." ) ; } this . projectRootPath = rootFolderPath ; this . iotWorkbenchProjectFilePath = path . join ( this . projectRootPath , FileNames . iotWorkbenchProjectFileName ) ; this . telemetryContext . properties . projectHostType = this . projectHostType ; } async load ( scaffoldType , initLoad = false ) { await this . validateProjectRootPathExists ( "load project" , scaffoldType ) ; await updateProjectHostTypeConfig ( scaffoldType , this . iotWorkbenchProjectFilePath , this . projectHostType ) ; if ( initLoad ) { this . sendLoadEventTelemetry ( this . extensionContext ) ; } const projectConfigJson = await getProjectConfig ( scaffoldType , this . iotWorkbenchProjectFilePath ) ; const boardId = projectConfigJson [ `${ ConfigKey . boardId }` ] ; if ( ! boardId ) { throw new ProjectConfigNotFoundError ( `${ ConfigKey . boardId }` , `${ this . iotWorkbenchProjectFilePath }` ) ; } await this . initDevice ( boardId , scaffoldType ) ; } async create ( templateFilesInfo , _projectType , boardId , openInNewWindow ) { await RemoteExtension . checkRemoteExtension ( "create iot containerized project" ) ; const createTimeScaffoldType = ScaffoldType . Local ; if ( ! ( await FileUtility . directoryExists ( createTimeScaffoldType , this . projectRootPath ) ) ) { await FileUtility . mkdirRecursively ( createTimeScaffoldType , this . projectRootPath ) ; } await updateProjectHostTypeConfig ( createTimeScaffoldType , this . iotWorkbenchProjectFilePath , this . projectHostType ) ; const projectConfig = await getProjectConfig ( createTimeScaffoldType , this . iotWorkbenchProjectFilePath ) ; await this . initDevice ( boardId , createTimeScaffoldType , templateFilesInfo ) ; projectConfig [ `${ ConfigKey . boardId }` ] = boardId ; if ( ! this . iotWorkbenchProjectFilePath ) { throw new ArgumentEmptyOrNullError ( "update workspace configuration" , "iot workbench project file" , "Please initialize the project first." ) ; } await FileUtility . writeJsonFile ( createTimeScaffoldType , this . iotWorkbenchProjectFilePath , projectConfig ) ; await Promise . all ( this . componentList . map ( async ( component ) => { await component . checkPrerequisites ( "create iot containerized project" ) ; } ) ) ; try { for ( let i = 0 ; i < this . componentList . length ; i ++ ) { await this . componentList [ i ] . create ( ) ; } } catch ( error ) { fs . removeSync ( this . projectRootPath ) ; throw error ; } await this . openProject ( createTimeScaffoldType , openInNewWindow , OpenScenario . createNewProject ) ; } /**
     * Ask user whether to open project in container directly.
     * If yes, open project in container. If not, stay local.
     */ async openProject ( scaffoldType , openInNewWindow , openScenario ) { await this . validateProjectRootPathExists ( "open project" , scaffoldType ) ; let openInContainer = false ; openInContainer = await this . askToOpenInContainer ( ) ; this . telemetryContext . properties . openInContainer = openInContainer . toString ( ) ; if ( ! openInNewWindow || openInContainer ) { try { const telemetryWorker = TelemetryWorker . getInstance ( this . extensionContext ) ; const eventNames = openScenario === OpenScenario . createNewProject ? EventNames . createNewProjectEvent : EventNames . configProjectEnvironmentEvent ; telemetryWorker . sendEvent ( eventNames , this . telemetryContext ) ; } catch { } } if ( openInContainer ) { await this . openFolderInContainer ( this . projectRootPath ) ; } else { await vscode . commands . executeCommand ( VscodeCommands . VscodeOpenFolder , vscode . Uri . file ( this . projectRootPath ) , openInNewWindow ) ; } } private async openFolderInContainer ( folderPath ) { if ( ! ( await FileUtility . directoryExists ( ScaffoldType . Local , folderPath ) ) ) { throw new DirectoryNotFoundError ( "open folder in container" , `folder path ${ folderPath }` , "" ) ; } await RemoteExtension . checkRemoteExtension ( "open folder in container" ) ; await vscode . commands . executeCommand ( RemoteContainersCommands . OpenFolder , vscode . Uri . file ( folderPath ) ) ; } param boardId param scaffoldType param templateFilesInfo private async initDevice ( boardId , scaffoldType , templateFilesInfo ? ) { await this . validateProjectRootPathExists ( "initialize device" , scaffoldType ) ; let device ; if ( boardId === raspberryPiDeviceModule . RaspberryPiDevice . boardId ) { device = new raspberryPiDeviceModule . RaspberryPiDevice ( this . extensionContext , this . projectRootPath , this . channel , this . telemetryContext , templateFilesInfo ) ; } else { throw new TypeNotSupportedError ( "board type" , boardId ) ; } if ( device ) { this . componentList . push ( device ) ; await device . load ( ) ; } } returns private async askToOpenInContainer ( ) { const openInContainerOption = [ ] ; openInContainerOption . push ( { label : `Yes` , detail : "I want to work on this project in container now." } , { label : `No` , detail : "I need to customize my container first locally." } ) ; const openInContainerSelection = await vscode . window . showQuickPick ( openInContainerOption , { ignoreFocusOut : true , placeHolder : `Do you want to open in container?` } ) ; if ( ! openInContainerSelection ) { throw new OperationCanceledError ( `Ask to customize development environment selection cancelled.` ) ; } return openInContainerSelection . label === "Yes" ; } }