diff --git a/package-lock.json b/package-lock.json index 8c60af43..36989c00 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,6 @@ "neverthrow": "^8.2.0", "open": "^8.4.0", "prettier": "^2.8.8", - "simple-git": "^3.27.0", "striptags": "^3.2.0", "tmp-promise": "^3.0.3", "treeify": "^1.1.0", @@ -4126,17 +4125,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@kwsites/file-exists": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "debug": "^4.1.1" - } - }, - "node_modules/@kwsites/promise-deferred": { - "version": "1.1.1", - "license": "MIT" - }, "node_modules/@mswjs/interceptors": { "version": "0.38.6", "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.38.6.tgz", @@ -12765,19 +12753,6 @@ "dev": true, "license": "ISC" }, - "node_modules/simple-git": { - "version": "3.27.0", - "license": "MIT", - "dependencies": { - "@kwsites/file-exists": "^1.1.1", - "@kwsites/promise-deferred": "^1.1.1", - "debug": "^4.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/steveukx/git-js?sponsor=1" - } - }, "node_modules/sinon": { "version": "21.0.0", "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", diff --git a/package.json b/package.json index bfca1b02..74c23eae 100644 --- a/package.json +++ b/package.json @@ -70,7 +70,6 @@ "neverthrow": "^8.2.0", "open": "^8.4.0", "prettier": "^2.8.8", - "simple-git": "^3.27.0", "striptags": "^3.2.0", "tmp-promise": "^3.0.3", "treeify": "^1.1.0", diff --git a/src/config/env.ts b/src/config/env.ts index f186b9da..2cd2be06 100644 --- a/src/config/env.ts +++ b/src/config/env.ts @@ -1,5 +1,4 @@ export const baseURL = "https://api.apimatic.io"; -export const staticPortalRepoUrl = "https://github.com/apimatic/static-portal-workflow.git"; export const metadataFileContent = { ImportSettings: { AutoGenerateTestCases: false, diff --git a/src/controllers/portal/quickstart.ts b/src/controllers/portal/quickstart.ts index ffac3dff..1202cb8f 100644 --- a/src/controllers/portal/quickstart.ts +++ b/src/controllers/portal/quickstart.ts @@ -1,4 +1,3 @@ -import { simpleGit } from "simple-git"; import axios from "axios"; import * as path from "path"; import filetype from "file-type"; @@ -18,18 +17,20 @@ import { } from "../../utils/utils.js"; import { getValidationSummary } from "../api/validate.js"; import { AuthorizationError, GetValidationParams } from "../../types/api/validate.js"; -import { metadataFileContent, staticPortalRepoUrl } from "../../config/env.js"; +import { metadataFileContent } from "../../config/env.js"; import { PortalQuickstartPrompts } from "../../prompts/portal/quickstart.js"; import { AuthenticationError } from "../../types/utils.js"; import { ZipService } from "../../infrastructure/zip-service.js"; import { FilePath } from "../../types/file/filePath.js"; import { DirectoryPath } from "../../types/file/directoryPath.js"; import { FileName } from "../../types/file/fileName.js"; +import { withDirPath } from "../../infrastructure/tmp-extensions.js"; +import { FileService } from "../../infrastructure/file-service.js"; export class PortalQuickstartController { private readonly zipService = new ZipService(); - private readonly specUrl = - "https://github.com/apimatic/static-portal-workflow/blob/master/spec/openapi.json"; + private readonly fileService = new FileService(); + private readonly specUrl = "https://github.com/apimatic/static-portal-workflow/blob/master/spec/openapi.json"; async isUserAuthenticated(configDir: string): Promise { const storedAuth = await getAuthInfo(configDir); @@ -114,10 +115,13 @@ export class PortalQuickstartController { } } else { specPath = path.normalize(specPath); - const fileType = await filetype.fromFile(specPath); + const fileType = await filetype.fromFile(specPath); if (fileType?.ext === "zip") { - await this.zipService.unArchive(new FilePath(new DirectoryPath(path.dirname(specPath)), new FileName(path.basename(specPath))), new DirectoryPath(tempSpecDir)); + await this.zipService.unArchive( + new FilePath(new DirectoryPath(path.dirname(specPath)), new FileName(path.basename(specPath))), + new DirectoryPath(tempSpecDir) + ); } else { const destinationPath = path.join(tempSpecDir, path.basename(specPath)); await fsExtra.copy(specPath, destinationPath); @@ -208,6 +212,26 @@ export class PortalQuickstartController { } } + private async downloadRepositoryFromGitHub(targetFolder: string): Promise { + return await withDirPath(async (tempDirectory) => { + const zipUrl = `https://github.com/apimatic/static-portal-workflow/archive/refs/heads/master.zip`; + const response = await fetch(zipUrl); + const repositoryFolderName = "static-portal-workflow-master"; + + if (!response.ok) { + throw new Error(`Unable to setup your portal, please try again later.`); + } + const arrayBuffer = await response.arrayBuffer(); + const tempZipPath = new FilePath(tempDirectory, new FileName("static-repo.zip")); + await this.fileService.writeBuffer(tempZipPath, Buffer.from(arrayBuffer)); + + await this.zipService.unArchive(tempZipPath, tempDirectory); + + const extractedFolderPath = new DirectoryPath(tempDirectory.toString(), repositoryFolderName); + await this.fileService.copyDirectoryContents(extractedFolderPath, new DirectoryPath(targetFolder)); + }); + } + async setupBuildDirectory( prompts: PortalQuickstartPrompts, targetFolder: string, @@ -215,20 +239,14 @@ export class PortalQuickstartController { validationSummary: ApiValidationSummary, languages: string[] ): Promise { - const git = simpleGit({ - timeout: { - block: 60 * 1000 // 1 minute timeout. - } - }); - fsExtra.emptyDirSync(targetFolder); try { - await git.clone(staticPortalRepoUrl, targetFolder); + await this.downloadRepositoryFromGitHub(targetFolder); } catch (error) { prompts.displayBuildDirectoryGenerationErrorMessage(); if (error instanceof Error) { - if (error.message.includes("timed out")) { + if (error.message.includes("timed out") || error.message.includes("timeout")) { throw new Error( getMessageInRedColor( "The operation timed out while setting up the build directory. Please check your internet connection and try again." @@ -246,7 +264,6 @@ export class PortalQuickstartController { } } - await clearDirectory(path.join(targetFolder, ".git")); await clearDirectory(path.join(targetFolder, ".github")); if (specFile.localPath && validationSummary.success) { diff --git a/src/infrastructure/file-service.ts b/src/infrastructure/file-service.ts index c8fa88c3..b7bb2c82 100644 --- a/src/infrastructure/file-service.ts +++ b/src/infrastructure/file-service.ts @@ -1,5 +1,6 @@ import fs from "fs"; import fsExtra from "fs-extra"; +import * as path from "path"; import { FilePath } from "../types/file/filePath.js"; import { DirectoryPath } from "../types/file/directoryPath.js"; import { pipeline } from "stream"; @@ -38,6 +39,21 @@ export class FileService { await fsExtra.emptyDir(dir.toString()); // removes everything inside, keeps the dir } + public async copyDirectory(source: DirectoryPath, destination: DirectoryPath) { + await fsExtra.copy(source.toString(), destination.toString()); + } + + public async copyDirectoryContents(source: DirectoryPath, destination: DirectoryPath) { + const entries = await fsExtra.readdir(source.toString()); + await Promise.all( + entries.map(async (entry) => { + const srcEntry = path.join(source.toString(), entry); + const destEntry = path.join(destination.toString(), entry); + await fsExtra.copy(srcEntry, destEntry); + }) + ); + } + public async deleteFile(filePath: FilePath): Promise { const exists = await this.fileExists(filePath); if (exists) { @@ -62,6 +78,10 @@ export class FileService { await fsExtra.writeFile(filePath.toString(), contents, 'utf-8'); } + public async writeBuffer(filePath: FilePath, buffer: Buffer) { + await fsExtra.writeFile(filePath.toString(), buffer); + } + public async copy(source: FilePath, destination: FilePath) { await fsExtra.copyFile(source.toString(), destination.toString()); }