Просмотр исходного кода

Merge pull request #8359 from weseek/imprv/download-plugin-that-contain-slashes-in-the-branch-name

imprv: Allow plugin that contain slashes in the branch name to be installed
Yuki Takei 2 лет назад
Родитель
Сommit
d525c6542c

+ 39 - 1
apps/app/src/features/growi-plugin/server/models/vo/github-url.spec.ts

@@ -63,6 +63,44 @@ describe('archiveUrl()', () => {
     const { archiveUrl } = githubUrl;
 
     // then
-    expect(archiveUrl).toEqual('https://github.com/org/repos/archive/refs/heads/fix/bug.zip');
+    expect(archiveUrl).toEqual('https://github.com/org/repos/archive/refs/heads/fix%2Fbug.zip');
   });
 });
+
+describe('extractedArchiveDirName()', () => {
+
+  describe('certain characters in the branch name are converted to slashes, and if they are consecutive, they become a single hyphen', () => {
+    it.concurrent.each`
+      branchName
+      ${'a"\'!,;-=@`]<>|&{}()$%+#/b'}
+      ${'a---b'}
+    `("'$branchName'", ({ branchName }) => {
+      // setup
+      const githubUrl = new GitHubUrl('https://github.com/org/repos', branchName);
+
+      // when
+      const { extractedArchiveDirName } = githubUrl;
+
+      // then
+      expect(extractedArchiveDirName).toEqual('a-b');
+    });
+  });
+
+  describe('when no certain characters in the branch name', () => {
+    it.concurrent.each`
+      branchName
+      ${'a.b'}
+      ${'a_b'}
+    `("'$branchName'", ({ branchName }) => {
+      // setup
+      const githubUrl = new GitHubUrl('https://github.com/org/repos', branchName);
+
+      // when
+      const { extractedArchiveDirName } = githubUrl;
+
+      // then
+      expect(extractedArchiveDirName).toEqual(branchName);
+    });
+  });
+
+});

+ 8 - 1
apps/app/src/features/growi-plugin/server/models/vo/github-url.ts

@@ -2,6 +2,8 @@ import sanitize from 'sanitize-filename';
 
 // https://regex101.com/r/fK2rV3/1
 const githubReposIdPattern = new RegExp(/^\/([^/]+)\/([^/]+)$/);
+// https://regex101.com/r/YhZVsj/1
+const sanitizeChars = new RegExp(/[^a-zA-Z_.]+/g);
 
 export class GitHubUrl {
 
@@ -24,10 +26,15 @@ export class GitHubUrl {
   }
 
   get archiveUrl(): string {
-    const ghUrl = new URL(`/${this.organizationName}/${this.reposName}/archive/refs/heads/${this.branchName}.zip`, 'https://github.com');
+    const encodedBranchName = encodeURIComponent(this.branchName);
+    const ghUrl = new URL(`/${this.organizationName}/${this.reposName}/archive/refs/heads/${encodedBranchName}.zip`, 'https://github.com');
     return ghUrl.toString();
   }
 
+  get extractedArchiveDirName(): string {
+    return this._branchName.replaceAll(sanitizeChars, '-');
+  }
+
   constructor(url: string, branchName = 'main') {
 
     let matched;

+ 6 - 9
apps/app/src/features/growi-plugin/server/services/growi-plugin/growi-plugin.ts

@@ -8,7 +8,6 @@ import { importPackageJson, validateGrowiDirective } from '@growi/pluginkit/dist
 // eslint-disable-next-line no-restricted-imports
 import axios from 'axios';
 import mongoose from 'mongoose';
-import sanitize from 'sanitize-filename';
 import streamToPromise from 'stream-to-promise';
 import unzipStream from 'unzip-stream';
 
@@ -77,11 +76,11 @@ export class GrowiPluginService implements IGrowiPluginService {
 
           // TODO: imprv Document version and repository version possibly different.
           const ghUrl = new GitHubUrl(growiPlugin.origin.url, growiPlugin.origin.ghBranch);
-          const { reposName, branchName, archiveUrl } = ghUrl;
+          const { reposName, archiveUrl, extractedArchiveDirName } = ghUrl;
 
-          const zipFilePath = path.join(PLUGIN_STORING_PATH, `${branchName}.zip`);
+          const zipFilePath = path.join(PLUGIN_STORING_PATH, `${extractedArchiveDirName}.zip`);
           const unzippedPath = PLUGIN_STORING_PATH;
-          const unzippedReposPath = path.join(PLUGIN_STORING_PATH, `${reposName}-${branchName}`);
+          const unzippedReposPath = path.join(PLUGIN_STORING_PATH, `${reposName}-${extractedArchiveDirName}`);
 
           try {
             // download github repository to local file system
@@ -111,16 +110,14 @@ export class GrowiPluginService implements IGrowiPluginService {
   async install(origin: IGrowiPluginOrigin): Promise<string> {
     const ghUrl = new GitHubUrl(origin.url, origin.ghBranch);
     const {
-      organizationName, reposName, branchName, archiveUrl,
+      organizationName, reposName, archiveUrl, extractedArchiveDirName,
     } = ghUrl;
 
-    const sanitizedBranchName = sanitize(branchName);
-
     const installedPath = `${organizationName}/${reposName}`;
 
     const organizationPath = path.join(PLUGIN_STORING_PATH, organizationName);
-    const zipFilePath = path.join(organizationPath, `${reposName}-${sanitizedBranchName}.zip`);
-    const temporaryReposPath = path.join(organizationPath, `${reposName}-${sanitizedBranchName}`);
+    const zipFilePath = path.join(organizationPath, `${reposName}-${extractedArchiveDirName}.zip`);
+    const temporaryReposPath = path.join(organizationPath, `${reposName}-${extractedArchiveDirName}`);
     const reposPath = path.join(organizationPath, reposName);
 
     if (!fs.existsSync(organizationPath)) fs.mkdirSync(organizationPath);