From 3f9bfd002bf390d0d616aedefefa7e872dfa547c Mon Sep 17 00:00:00 2001 From: fraxken Date: Thu, 15 Aug 2024 16:23:00 +0200 Subject: [PATCH 1/3] test: split for readability and use mkdtemp & os.tmpdir --- test/download.spec.ts | 79 +++++++++++++++++++ test/downloadAndExtract.spec.ts | 69 +++++++++++++++++ test/index.spec.ts | 132 -------------------------------- 3 files changed, 148 insertions(+), 132 deletions(-) create mode 100644 test/download.spec.ts create mode 100644 test/downloadAndExtract.spec.ts delete mode 100644 test/index.spec.ts diff --git a/test/download.spec.ts b/test/download.spec.ts new file mode 100644 index 0000000..88a6dd6 --- /dev/null +++ b/test/download.spec.ts @@ -0,0 +1,79 @@ +// Import Node.js Dependencies +import path from "node:path"; +import os from "node:os"; +import fs from "node:fs"; +import assert from "node:assert"; +import { describe, after, test, before } from "node:test"; + +// Import Third-party Dependencies +import is from "@slimio/is"; + +// Import Internal Dependencies +import * as github from "../src/index.js"; + +describe("download", () => { + let tempDownloadDir: string; + + before(() => { + tempDownloadDir = fs.mkdtempSync( + path.join(os.tmpdir(), "nsecure-gitlab-") + ); + }); + + after(() => { + fs.rmSync(tempDownloadDir, { recursive: true, force: true }); + }); + + test("should export as an asyncFunction", () => { + assert.ok(is.func(github.download)); + assert.ok(is.asyncFunction(github.download)); + }); + + test("download must throw: repository must be a string!", () => { + assert.rejects( + async() => await github.download(10 as any), + { + name: "TypeError", + message: "repository must be a string!" + } + ); + }); + + test("download public repository (without extraction)", async() => { + const { location, repository, organization } = await github.download("SlimIO.Config", { + dest: tempDownloadDir, + branch: "master" + }); + assert.strictEqual(repository, "Config"); + assert.strictEqual(organization, "SlimIO"); + assert.strictEqual(location, path.join(tempDownloadDir, "Config-master.tar.gz")); + + fs.accessSync(location); + fs.unlinkSync(location); + }); + + test("download public repository (at current working dir)", async() => { + const { location } = await github.download("NodeSecure.utils"); + assert.strictEqual( + location, + path.join(process.cwd(), "utils-main.tar.gz") + ); + + fs.accessSync(location); + fs.unlinkSync(location); + }); + + test("download private repository (without extraction)", async() => { + const { location } = await github.download("SlimIO.Core", { + dest: tempDownloadDir, + branch: "master" + }); + assert.strictEqual( + location, + path.join(tempDownloadDir, "Core-master.tar.gz") + ); + + fs.accessSync(location); + fs.unlinkSync(location); + }); +}); diff --git a/test/downloadAndExtract.spec.ts b/test/downloadAndExtract.spec.ts new file mode 100644 index 0000000..0566043 --- /dev/null +++ b/test/downloadAndExtract.spec.ts @@ -0,0 +1,69 @@ +// Import Node.js Dependencies +import path from "node:path"; +import os from "node:os"; +import fs from "node:fs"; +import assert from "node:assert"; +import { describe, after, test, before } from "node:test"; + +// Import Third-party Dependencies +import is from "@slimio/is"; + +// Import Internal Dependencies +import * as github from "../src/index.js"; + +describe("downloadAndExtract", () => { + let tempDownloadDir: string; + + before(() => { + tempDownloadDir = fs.mkdtempSync( + path.join(os.tmpdir(), "nsecure-gitlab-") + ); + }); + + after(() => { + fs.rmSync(tempDownloadDir, { recursive: true, force: true }); + }); + + test("should export as an asyncFunction", () => { + assert.ok(is.func(github.downloadAndExtract)); + assert.ok(is.asyncFunction(github.downloadAndExtract)); + }); + + test("download public repository (with extraction)", async() => { + const result = await github.downloadAndExtract("SlimIO.is", { + dest: tempDownloadDir, + branch: "master" + }); + + assert.strictEqual( + result.location, + path.join(tempDownloadDir, "is-master") + ); + assert.ok(fs.statSync(result.location).isDirectory()); + assert.throws( + () => fs.accessSync(path.join(tempDownloadDir, "is-master.tar.gz")), + { + name: "Error", + code: "ENOENT", + message: /no such file or directory/ + } + ); + }); + + test("download public repository (with extraction and removeArchive disabled)", async() => { + const { location } = await github.downloadAndExtract("SlimIO.Safe-emitter", { + dest: tempDownloadDir, + branch: "master", + removeArchive: false + }); + + fs.accessSync( + path.join(tempDownloadDir, "Safe-emitter-master.tar.gz") + ); + assert.strictEqual( + location, + path.join(tempDownloadDir, "Safe-emitter-master") + ); + assert.ok(fs.statSync(location).isDirectory()); + }); +}); diff --git a/test/index.spec.ts b/test/index.spec.ts deleted file mode 100644 index bf4fdf9..0000000 --- a/test/index.spec.ts +++ /dev/null @@ -1,132 +0,0 @@ -// Import Node.js Dependencies -import { join, dirname } from "node:path"; -import { fileURLToPath } from "node:url"; -import fs from "node:fs/promises"; -import { describe, before, test } from "node:test"; -import assert from "node:assert"; -import * as timers from "node:timers/promises"; - -// Import Third-party Dependencies -import is from "@slimio/is"; - -// Import Internal Dependencies -import * as github from "../src/index.js"; - -// CONSTANTS -const __dirname = dirname(fileURLToPath(import.meta.url)); - -test("github.download should be an asyncFunction", () => { - assert.equal(is.func(github.download), true); - assert.equal(is.asyncFunction(github.download), true); -}); - -test("github.downloadAndExtract should be an asyncFunction", () => { - assert.equal(is.func(github.downloadAndExtract), true); - assert.equal(is.asyncFunction(github.downloadAndExtract), true); -}); - -test("download must throw: repository must be a string!", () => { - assert.rejects( - async() => await github.download(10 as any), - { - name: "TypeError", - message: "repository must be a string!" - } - ); -}); - -test("download public repository (without extraction)", async() => { - const { location, repository, organization } = await github.download("SlimIO.Config", { - dest: __dirname, - branch: "master" - }); - assert.equal(repository, "Config"); - assert.equal(organization, "SlimIO"); - assert.equal(location, join(__dirname, "Config-master.tar.gz")); - - await fs.access(location); - await fs.unlink(location); -}); - -test("download public repository (at current working dir)", async() => { - const { location } = await github.download("NodeSecure.utils"); - assert.equal(location, join(process.cwd(), "utils-main.tar.gz")); - - await fs.access(location); - await fs.unlink(location); -}); - -test("download private repository (without extraction)", async() => { - const { location } = await github.download("SlimIO.Core", { - dest: __dirname, - branch: "master" - }); - assert.equal(location, join(__dirname, "Core-master.tar.gz")); - - await fs.access(location); - await fs.unlink(location); -}); - -describe("download public repository (with extraction)", () => { - let location; - - before(async() => { - const result = await github.downloadAndExtract("SlimIO.is", { - dest: __dirname, - branch: "master" - }); - location = result.location; - }); - - test("should have the correct download location", async() => { - assert.equal(location, join(__dirname, "is-master")); - }); - - test("should be a directory", async() => { - const st = await fs.stat(location); - assert.ok(st.isDirectory()); - }); - - test("should not have the tar.gz file", async() => { - const filePath = join(__dirname, "is-master.tar.gz"); - assert.rejects( - async() => await fs.access(filePath), - { - name: "Error", - code: "ENOENT", - message: /no such file or directory/ - } - ); - }); -}); - -test("download public repository (with extraction and removeArchive disabled)", async() => { - const { location } = await github.downloadAndExtract("SlimIO.Safe-emitter", { - dest: __dirname, - branch: "master", - removeArchive: false - }); - - await fs.access(join(__dirname, "Safe-emitter-master.tar.gz")); - assert.equal(location, join(__dirname, "Safe-emitter-master")); - assert.equal((await fs.stat(location)).isDirectory(), true); -}); - -test("teardown", async() => { - await timers.setTimeout(50); - - const dirents = await fs.readdir(__dirname, { withFileTypes: true }); - for (const dirent of dirents) { - if (dirent.name === "index.spec.ts") { - continue; - } - - const fullPath = join(__dirname, dirent.name); - if (dirent.isDirectory()) { - await fs.rm(fullPath, { recursive: true, force: true }); - } - else { - await fs.unlink(fullPath); - } - } -}); From 2245e6c7925e7f055d219d8747337c5e8d7542b2 Mon Sep 17 00:00:00 2001 From: fraxken Date: Thu, 15 Aug 2024 16:23:48 +0200 Subject: [PATCH 2/3] ci: automatically merge dependabot PR --- .github/workflows/node.js.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.github/workflows/node.js.yml b/.github/workflows/node.js.yml index 9d5fed9..a8fbc90 100644 --- a/.github/workflows/node.js.yml +++ b/.github/workflows/node.js.yml @@ -32,3 +32,17 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.REPO_SECRET }} run: npm run test + automerge: + if: > + github.event_name == 'pull_request' && github.event.pull_request.user.login == 'dependabot[bot]' + needs: + - test + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Merge Dependabot PR + uses: fastify/github-action-merge-dependabot@9e7bfb249c69139d7bdcd8d984f9665edd49020b # v3.10.1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} From 666111fe7a31b635d8b39089f91e492f56045ac1 Mon Sep 17 00:00:00 2001 From: fraxken Date: Thu, 15 Aug 2024 16:26:49 +0200 Subject: [PATCH 3/3] docs: improve current API --- README.md | 28 ++++++++++++++++------------ src/functions/download.ts | 7 +++++-- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ba72c9f..59866ea 100644 --- a/README.md +++ b/README.md @@ -37,8 +37,10 @@ console.log(scanner.location); ## API +Both `download` and `downloadAndExtract` functions use the same set of options. + ```ts -export interface DownloadOptions { +interface DownloadOptions { /** * The destination (location) to extract the tar.gz * @@ -58,22 +60,29 @@ export interface DownloadOptions { */ token?: string; } +``` -export interface DownloadResult { +### download(repository: string, options?: DownloadOptions): Promise< DownloadResult > +Download the tar.gz archive of the GIT repository. + +```ts +interface DownloadResult { /** Archive or repository location on disk */ location: string; /** Github repository name */ repository: string; /** Github organization name */ organization: string; + /** Github branch name */ + branch: string; } +``` -export function download( - repo: string, - options?: DownloadOptions -): Promise; +### downloadAndExtract(repository: string, options?: DownloadExtractOptions): Promise< DownloadResult > +Use download but extract the tar.gz archive. -export interface DownloadExtractOptions extends DownloadOptions { +```ts +interface DownloadExtractOptions extends DownloadOptions { /** * Remove the tar.gz archive after a succesfull extraction * @@ -81,11 +90,6 @@ export interface DownloadExtractOptions extends DownloadOptions { */ removeArchive?: boolean; } - -export function downloadAndExtract( - repo: string, - options?: DownloadExtractOptions -): Promise; ``` ## Contributors ✨ diff --git a/src/functions/download.ts b/src/functions/download.ts index 4087112..6fc40f0 100644 --- a/src/functions/download.ts +++ b/src/functions/download.ts @@ -37,6 +37,8 @@ export interface DownloadResult { repository: string; /** Github organization name */ organization: string; + /** Github branch name */ + branch: string; } /** @@ -67,7 +69,7 @@ export async function download( headers: { "User-Agent": "NodeSecure", "Accept-Encoding": "gzip, deflate", - Authorization: `token ${token}` + Authorization: typeof token === "string" ? `token ${token}` : void 0 }, maxRedirections: 1 }); @@ -76,6 +78,7 @@ export async function download( return { location, organization, - repository: repo + repository: repo, + branch }; }