Skip to content

Commit

Permalink
feat: ✨ server download its plugins by himself
Browse files Browse the repository at this point in the history
  • Loading branch information
ArnaudTA committed Sep 11, 2024
1 parent e027398 commit 6033843
Show file tree
Hide file tree
Showing 5 changed files with 352 additions and 67 deletions.
2 changes: 2 additions & 0 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"@ts-rest/open-api": "^3.45.2",
"axios": "^1.7.2",
"date-fns": "^3.6.0",
"decompress": "^4.2.1",
"dotenv": "^16.4.5",
"fastify": "^4.28.0",
"fastify-keycloak-adapter": "^2.1.10",
Expand All @@ -66,6 +67,7 @@
"nanoid": "5.0.7",
"node-vault-client": "^1.0.1",
"prisma": "^5.16.1",
"semver": "^7.6.3",
"undici": "^6.19.2",
"vitest-mock-extended": "^1.3.1"
},
Expand Down
103 changes: 102 additions & 1 deletion apps/server/src/plugins.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import { readdirSync, statSync } from 'node:fs'
import path from 'node:path'
import { createWriteStream, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, rmdirSync, statSync, unlinkSync } from 'node:fs'
import axios from 'axios'
// @ts-expect-error TS7016
import decompress from 'decompress'
// @ts-expect-error TS7016
import semver from 'semver'
import { type Plugin, pluginManager } from '@cpn-console/hooks'
import { plugin as argo } from '@cpn-console/argocd-plugin'
import { plugin as gitlab } from '@cpn-console/gitlab-plugin'
Expand All @@ -11,7 +17,101 @@ import { plugin as vault } from '@cpn-console/vault-plugin'
import { pluginManagerOptions } from './utils/plugins.js'
import { pluginsDir } from './utils/env.js'

async function downloadZip(url: string, savePath: string) {
// axios image download with response type "stream"
const response = await axios({
method: 'GET',
url,
responseType: 'stream',
})

// pipe the result stream into a file on disc
response.data.pipe(createWriteStream(savePath))

// return a promise and resolve when download finishes
return new Promise<string>((resolve, reject) => {
response.data.on('end', () => {
resolve(savePath)
})

response.data.on('error', () => {
// @ts-ignore
reject(new Error(`Unable to download archive`))
})
})
}

const hooksPackageJsonPath = path.join(
path.dirname(
path.dirname(
require.resolve('@cpn-console/hooks'),
),
),
'package.json',
)

const hooksPackageVersion = JSON.parse(readFileSync(hooksPackageJsonPath, 'utf-8')).version // 2.3.2

async function importZipPuglin(zipUrl: string, name: string, pluginsArchiveDir: string) {
const zipPath = path.join(pluginsArchiveDir, `${name}.zip`)
await downloadZip(zipUrl, zipPath)
const pluginPath = path.join(pluginsDir, name)
await decompress(zipPath, pluginPath)
}

async function getMappedVersion(mapperUrl: string) {
const mapper = await axios.get(mapperUrl)
let url
if (mapper.status !== 200) {
throw new Error(`unable to fetch mapper ${mapperUrl}`)
}
for (const [range, pluginUrl] of Object.entries(mapper.data)) {
if (semver.satisfies(hooksPackageVersion, range)) {
url = pluginUrl
break
}
}

if (!url) {
throw new Error('impossible de trouver une version correspondante')
}
return url as string
}

export async function fetchExternalPlugins(pluginUrls: string[]) {
if (!pluginUrls.length) return

const pluginsArchiveDir = mkdtempSync('/tmp/plugins')

console.log(pluginsArchiveDir)
if (!existsSync(pluginsDir)) {
mkdirSync(pluginsDir)
}

let i = 0
for (let pluginUrl of pluginUrls) {
if (pluginUrl.startsWith('!')) {
console.log('ignoring', pluginUrl)
continue
}
i++
if (pluginUrl.endsWith('.json')) {
pluginUrl = await getMappedVersion(pluginUrl)
}
await importZipPuglin(pluginUrl, `fetched-${i.toString()}`, pluginsArchiveDir)
}
const fetchedPlugins = readdirSync(pluginsArchiveDir)
console.log(fetchedPlugins)
for (const file of fetchedPlugins) {
unlinkSync(path.join(pluginsArchiveDir, file))
}
rmdirSync(pluginsArchiveDir)
}

const plugins = 'http://cool_leakey:8000/mapper.json'.split(',')

export async function initPm() {
const fetchPromise = fetchExternalPlugins(plugins)
const pm = pluginManager(pluginManagerOptions)
pm.register(argo)
pm.register(gitlab)
Expand All @@ -22,6 +122,7 @@ export async function initPm() {
pm.register(sonarqube)
pm.register(vault)

await fetchPromise
if (!statSync(pluginsDir, {
throwIfNoEntry: false,
})) {
Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/prepare-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ export async function getPreparedApp() {
throw error
}

initPm()
await initPm()

app.log.info('Reading init database file')

Expand Down
1 change: 0 additions & 1 deletion apps/server/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { getPreparedApp } from './prepare-app.js'
import { closeConnections } from './connect.js'

import { isCI, isDev, isDevSetup, isProd, isTest, port } from './utils/env.js'

const app = await getPreparedApp()
Expand Down
Loading

0 comments on commit 6033843

Please sign in to comment.