diff options
Diffstat (limited to 'server')
-rw-r--r-- | server/helpers/utils.ts | 26 | ||||
-rw-r--r-- | server/helpers/version.ts | 36 | ||||
-rw-r--r-- | server/initializers/constants.ts | 2 | ||||
-rw-r--r-- | server/initializers/installer.ts | 7 | ||||
-rw-r--r-- | server/initializers/migrations/0725-node-version.ts | 66 | ||||
-rw-r--r-- | server/lib/plugins/plugin-manager.ts | 9 | ||||
-rw-r--r-- | server/lib/plugins/yarn.ts | 5 | ||||
-rw-r--r-- | server/lib/server-config-manager.ts | 2 | ||||
-rw-r--r-- | server/models/application/application.ts | 22 | ||||
-rw-r--r-- | server/tests/api/server/plugins.ts | 32 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test-native/main.js | 21 | ||||
-rw-r--r-- | server/tests/fixtures/peertube-plugin-test-native/package.json | 23 |
12 files changed, 221 insertions, 30 deletions
diff --git a/server/helpers/utils.ts b/server/helpers/utils.ts index 6b9333b53..5a4fe4fdd 100644 --- a/server/helpers/utils.ts +++ b/server/helpers/utils.ts | |||
@@ -4,7 +4,7 @@ import { join } from 'path' | |||
4 | import { sha256 } from '@shared/extra-utils' | 4 | import { sha256 } from '@shared/extra-utils' |
5 | import { ResultList } from '@shared/models' | 5 | import { ResultList } from '@shared/models' |
6 | import { CONFIG } from '../initializers/config' | 6 | import { CONFIG } from '../initializers/config' |
7 | import { execPromise, execPromise2, randomBytesPromise } from './core-utils' | 7 | import { randomBytesPromise } from './core-utils' |
8 | import { logger } from './logger' | 8 | import { logger } from './logger' |
9 | 9 | ||
10 | function deleteFileAndCatch (path: string) { | 10 | function deleteFileAndCatch (path: string) { |
@@ -44,29 +44,6 @@ function getSecureTorrentName (originalName: string) { | |||
44 | return sha256(originalName) + '.torrent' | 44 | return sha256(originalName) + '.torrent' |
45 | } | 45 | } |
46 | 46 | ||
47 | async function getServerCommit () { | ||
48 | try { | ||
49 | const tag = await execPromise2( | ||
50 | '[ ! -d .git ] || git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || true', | ||
51 | { stdio: [ 0, 1, 2 ] } | ||
52 | ) | ||
53 | |||
54 | if (tag) return tag.replace(/^v/, '') | ||
55 | } catch (err) { | ||
56 | logger.debug('Cannot get version from git tags.', { err }) | ||
57 | } | ||
58 | |||
59 | try { | ||
60 | const version = await execPromise('[ ! -d .git ] || git rev-parse --short HEAD') | ||
61 | |||
62 | if (version) return version.toString().trim() | ||
63 | } catch (err) { | ||
64 | logger.debug('Cannot get version from git HEAD.', { err }) | ||
65 | } | ||
66 | |||
67 | return '' | ||
68 | } | ||
69 | |||
70 | /** | 47 | /** |
71 | * From a filename like "ede4cba5-742b-46fa-a388-9a6eb3a3aeb3.mp4", returns | 48 | * From a filename like "ede4cba5-742b-46fa-a388-9a6eb3a3aeb3.mp4", returns |
72 | * only the "ede4cba5-742b-46fa-a388-9a6eb3a3aeb3" part. If the filename does | 49 | * only the "ede4cba5-742b-46fa-a388-9a6eb3a3aeb3" part. If the filename does |
@@ -88,7 +65,6 @@ export { | |||
88 | generateRandomString, | 65 | generateRandomString, |
89 | getFormattedObjects, | 66 | getFormattedObjects, |
90 | getSecureTorrentName, | 67 | getSecureTorrentName, |
91 | getServerCommit, | ||
92 | generateVideoImportTmpPath, | 68 | generateVideoImportTmpPath, |
93 | getUUIDFromFilename | 69 | getUUIDFromFilename |
94 | } | 70 | } |
diff --git a/server/helpers/version.ts b/server/helpers/version.ts new file mode 100644 index 000000000..5b3bf59dd --- /dev/null +++ b/server/helpers/version.ts | |||
@@ -0,0 +1,36 @@ | |||
1 | import { execPromise, execPromise2 } from './core-utils' | ||
2 | import { logger } from './logger' | ||
3 | |||
4 | async function getServerCommit () { | ||
5 | try { | ||
6 | const tag = await execPromise2( | ||
7 | '[ ! -d .git ] || git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || true', | ||
8 | { stdio: [ 0, 1, 2 ] } | ||
9 | ) | ||
10 | |||
11 | if (tag) return tag.replace(/^v/, '') | ||
12 | } catch (err) { | ||
13 | logger.debug('Cannot get version from git tags.', { err }) | ||
14 | } | ||
15 | |||
16 | try { | ||
17 | const version = await execPromise('[ ! -d .git ] || git rev-parse --short HEAD') | ||
18 | |||
19 | if (version) return version.toString().trim() | ||
20 | } catch (err) { | ||
21 | logger.debug('Cannot get version from git HEAD.', { err }) | ||
22 | } | ||
23 | |||
24 | return '' | ||
25 | } | ||
26 | |||
27 | function getNodeABIVersion () { | ||
28 | const version = process.versions.modules | ||
29 | |||
30 | return parseInt(version) | ||
31 | } | ||
32 | |||
33 | export { | ||
34 | getServerCommit, | ||
35 | getNodeABIVersion | ||
36 | } | ||
diff --git a/server/initializers/constants.ts b/server/initializers/constants.ts index 99ae64f8d..8165a289d 100644 --- a/server/initializers/constants.ts +++ b/server/initializers/constants.ts | |||
@@ -24,7 +24,7 @@ import { CONFIG, registerConfigChangedHandler } from './config' | |||
24 | 24 | ||
25 | // --------------------------------------------------------------------------- | 25 | // --------------------------------------------------------------------------- |
26 | 26 | ||
27 | const LAST_MIGRATION_VERSION = 720 | 27 | const LAST_MIGRATION_VERSION = 725 |
28 | 28 | ||
29 | // --------------------------------------------------------------------------- | 29 | // --------------------------------------------------------------------------- |
30 | 30 | ||
diff --git a/server/initializers/installer.ts b/server/initializers/installer.ts index 7d5919459..b02be9567 100644 --- a/server/initializers/installer.ts +++ b/server/initializers/installer.ts | |||
@@ -1,6 +1,8 @@ | |||
1 | import { ensureDir, readdir, remove } from 'fs-extra' | 1 | import { ensureDir, readdir, remove } from 'fs-extra' |
2 | import passwordGenerator from 'password-generator' | 2 | import passwordGenerator from 'password-generator' |
3 | import { join } from 'path' | 3 | import { join } from 'path' |
4 | import { isTestOrDevInstance } from '@server/helpers/core-utils' | ||
5 | import { getNodeABIVersion } from '@server/helpers/version' | ||
4 | import { UserRole } from '@shared/models' | 6 | import { UserRole } from '@shared/models' |
5 | import { logger } from '../helpers/logger' | 7 | import { logger } from '../helpers/logger' |
6 | import { buildUser, createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user' | 8 | import { buildUser, createApplicationActor, createUserAccountAndChannelAndPlaylist } from '../lib/user' |
@@ -10,7 +12,6 @@ import { applicationExist, clientsExist, usersExist } from './checker-after-init | |||
10 | import { CONFIG } from './config' | 12 | import { CONFIG } from './config' |
11 | import { FILES_CACHE, HLS_STREAMING_PLAYLIST_DIRECTORY, LAST_MIGRATION_VERSION, RESUMABLE_UPLOAD_DIRECTORY } from './constants' | 13 | import { FILES_CACHE, HLS_STREAMING_PLAYLIST_DIRECTORY, LAST_MIGRATION_VERSION, RESUMABLE_UPLOAD_DIRECTORY } from './constants' |
12 | import { sequelizeTypescript } from './database' | 14 | import { sequelizeTypescript } from './database' |
13 | import { isTestOrDevInstance } from '@server/helpers/core-utils' | ||
14 | 15 | ||
15 | async function installApplication () { | 16 | async function installApplication () { |
16 | try { | 17 | try { |
@@ -175,7 +176,9 @@ async function createApplicationIfNotExist () { | |||
175 | logger.info('Creating application account.') | 176 | logger.info('Creating application account.') |
176 | 177 | ||
177 | const application = await ApplicationModel.create({ | 178 | const application = await ApplicationModel.create({ |
178 | migrationVersion: LAST_MIGRATION_VERSION | 179 | migrationVersion: LAST_MIGRATION_VERSION, |
180 | nodeVersion: process.version, | ||
181 | nodeABIVersion: getNodeABIVersion() | ||
179 | }) | 182 | }) |
180 | 183 | ||
181 | return createApplicationActor(application.id) | 184 | return createApplicationActor(application.id) |
diff --git a/server/initializers/migrations/0725-node-version.ts b/server/initializers/migrations/0725-node-version.ts new file mode 100644 index 000000000..d8b9cc750 --- /dev/null +++ b/server/initializers/migrations/0725-node-version.ts | |||
@@ -0,0 +1,66 @@ | |||
1 | import * as Sequelize from 'sequelize' | ||
2 | |||
3 | async function up (utils: { | ||
4 | transaction: Sequelize.Transaction | ||
5 | queryInterface: Sequelize.QueryInterface | ||
6 | sequelize: Sequelize.Sequelize | ||
7 | db: any | ||
8 | }): Promise<void> { | ||
9 | const { transaction } = utils | ||
10 | |||
11 | { | ||
12 | const data = { | ||
13 | type: Sequelize.STRING, | ||
14 | defaultValue: null, | ||
15 | allowNull: true | ||
16 | } | ||
17 | await utils.queryInterface.addColumn('application', 'nodeVersion', data, { transaction }) | ||
18 | } | ||
19 | |||
20 | { | ||
21 | const data = { | ||
22 | type: Sequelize.STRING, | ||
23 | defaultValue: null, | ||
24 | allowNull: true | ||
25 | } | ||
26 | await utils.queryInterface.addColumn('application', 'nodeABIVersion', data, { transaction }) | ||
27 | } | ||
28 | |||
29 | { | ||
30 | const query = `UPDATE "application" SET "nodeVersion" = '${process.version}'` | ||
31 | await utils.sequelize.query(query, { transaction }) | ||
32 | } | ||
33 | |||
34 | { | ||
35 | const nodeABIVersion = parseInt(process.versions.modules) | ||
36 | const query = `UPDATE "application" SET "nodeABIVersion" = ${nodeABIVersion}` | ||
37 | await utils.sequelize.query(query, { transaction }) | ||
38 | } | ||
39 | |||
40 | { | ||
41 | const data = { | ||
42 | type: Sequelize.STRING, | ||
43 | defaultValue: null, | ||
44 | allowNull: false | ||
45 | } | ||
46 | await utils.queryInterface.changeColumn('application', 'nodeVersion', data, { transaction }) | ||
47 | } | ||
48 | |||
49 | { | ||
50 | const data = { | ||
51 | type: Sequelize.STRING, | ||
52 | defaultValue: null, | ||
53 | allowNull: false | ||
54 | } | ||
55 | await utils.queryInterface.changeColumn('application', 'nodeABIVersion', data, { transaction }) | ||
56 | } | ||
57 | } | ||
58 | |||
59 | function down (options) { | ||
60 | throw new Error('Not implemented.') | ||
61 | } | ||
62 | |||
63 | export { | ||
64 | up, | ||
65 | down | ||
66 | } | ||
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts index a706df1e0..a46b97fa4 100644 --- a/server/lib/plugins/plugin-manager.ts +++ b/server/lib/plugins/plugin-manager.ts | |||
@@ -3,6 +3,7 @@ import { createReadStream, createWriteStream } from 'fs' | |||
3 | import { ensureDir, outputFile, readJSON } from 'fs-extra' | 3 | import { ensureDir, outputFile, readJSON } from 'fs-extra' |
4 | import { basename, join } from 'path' | 4 | import { basename, join } from 'path' |
5 | import { decachePlugin } from '@server/helpers/decache' | 5 | import { decachePlugin } from '@server/helpers/decache' |
6 | import { ApplicationModel } from '@server/models/application/application' | ||
6 | import { MOAuthTokenUser, MUser } from '@server/types/models' | 7 | import { MOAuthTokenUser, MUser } from '@server/types/models' |
7 | import { getCompleteLocale } from '@shared/core-utils' | 8 | import { getCompleteLocale } from '@shared/core-utils' |
8 | import { | 9 | import { |
@@ -23,7 +24,7 @@ import { PluginModel } from '../../models/server/plugin' | |||
23 | import { PluginLibrary, RegisterServerAuthExternalOptions, RegisterServerAuthPassOptions, RegisterServerOptions } from '../../types/plugins' | 24 | import { PluginLibrary, RegisterServerAuthExternalOptions, RegisterServerAuthPassOptions, RegisterServerOptions } from '../../types/plugins' |
24 | import { ClientHtml } from '../client-html' | 25 | import { ClientHtml } from '../client-html' |
25 | import { RegisterHelpers } from './register-helpers' | 26 | import { RegisterHelpers } from './register-helpers' |
26 | import { installNpmPlugin, installNpmPluginFromDisk, removeNpmPlugin } from './yarn' | 27 | import { installNpmPlugin, installNpmPluginFromDisk, rebuildNativePlugins, removeNpmPlugin } from './yarn' |
27 | 28 | ||
28 | export interface RegisteredPlugin { | 29 | export interface RegisteredPlugin { |
29 | npmName: string | 30 | npmName: string |
@@ -384,6 +385,12 @@ export class PluginManager implements ServerHook { | |||
384 | logger.info('Plugin %s uninstalled.', npmName) | 385 | logger.info('Plugin %s uninstalled.', npmName) |
385 | } | 386 | } |
386 | 387 | ||
388 | async rebuildNativePluginsIfNeeded () { | ||
389 | if (!await ApplicationModel.nodeABIChanged()) return | ||
390 | |||
391 | return rebuildNativePlugins() | ||
392 | } | ||
393 | |||
387 | // ###################### Private register ###################### | 394 | // ###################### Private register ###################### |
388 | 395 | ||
389 | private async registerPluginOrTheme (plugin: PluginModel) { | 396 | private async registerPluginOrTheme (plugin: PluginModel) { |
diff --git a/server/lib/plugins/yarn.ts b/server/lib/plugins/yarn.ts index 3f45681d3..d105b95e0 100644 --- a/server/lib/plugins/yarn.ts +++ b/server/lib/plugins/yarn.ts | |||
@@ -31,11 +31,16 @@ async function removeNpmPlugin (name: string) { | |||
31 | await execYarn('remove ' + name) | 31 | await execYarn('remove ' + name) |
32 | } | 32 | } |
33 | 33 | ||
34 | async function rebuildNativePlugins () { | ||
35 | await execYarn('install --pure-lockfile') | ||
36 | } | ||
37 | |||
34 | // ############################################################################ | 38 | // ############################################################################ |
35 | 39 | ||
36 | export { | 40 | export { |
37 | installNpmPlugin, | 41 | installNpmPlugin, |
38 | installNpmPluginFromDisk, | 42 | installNpmPluginFromDisk, |
43 | rebuildNativePlugins, | ||
39 | removeNpmPlugin | 44 | removeNpmPlugin |
40 | } | 45 | } |
41 | 46 | ||
diff --git a/server/lib/server-config-manager.ts b/server/lib/server-config-manager.ts index d16a88f65..a3312fa20 100644 --- a/server/lib/server-config-manager.ts +++ b/server/lib/server-config-manager.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { getServerCommit } from '@server/helpers/utils' | 1 | import { getServerCommit } from '@server/helpers/version' |
2 | import { CONFIG, isEmailEnabled } from '@server/initializers/config' | 2 | import { CONFIG, isEmailEnabled } from '@server/initializers/config' |
3 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants' | 3 | import { CONSTRAINTS_FIELDS, DEFAULT_THEME_NAME, PEERTUBE_VERSION } from '@server/initializers/constants' |
4 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '@server/lib/signup' | 4 | import { isSignupAllowed, isSignupAllowedForCurrentIP } from '@server/lib/signup' |
diff --git a/server/models/application/application.ts b/server/models/application/application.ts index a479de5d2..d4590e001 100644 --- a/server/models/application/application.ts +++ b/server/models/application/application.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import memoizee from 'memoizee' | 1 | import memoizee from 'memoizee' |
2 | import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' | 2 | import { AllowNull, Column, Default, DefaultScope, HasOne, IsInt, Model, Table } from 'sequelize-typescript' |
3 | import { getNodeABIVersion } from '@server/helpers/version' | ||
3 | import { AttributesOnly } from '@shared/typescript-utils' | 4 | import { AttributesOnly } from '@shared/typescript-utils' |
4 | import { AccountModel } from '../account/account' | 5 | import { AccountModel } from '../account/account' |
5 | 6 | ||
@@ -37,6 +38,14 @@ export class ApplicationModel extends Model<Partial<AttributesOnly<ApplicationMo | |||
37 | @Column | 38 | @Column |
38 | latestPeerTubeVersion: string | 39 | latestPeerTubeVersion: string |
39 | 40 | ||
41 | @AllowNull(false) | ||
42 | @Column | ||
43 | nodeVersion: string | ||
44 | |||
45 | @AllowNull(false) | ||
46 | @Column | ||
47 | nodeABIVersion: number | ||
48 | |||
40 | @HasOne(() => AccountModel, { | 49 | @HasOne(() => AccountModel, { |
41 | foreignKey: { | 50 | foreignKey: { |
42 | allowNull: true | 51 | allowNull: true |
@@ -52,4 +61,17 @@ export class ApplicationModel extends Model<Partial<AttributesOnly<ApplicationMo | |||
52 | static load () { | 61 | static load () { |
53 | return ApplicationModel.findOne() | 62 | return ApplicationModel.findOne() |
54 | } | 63 | } |
64 | |||
65 | static async nodeABIChanged () { | ||
66 | const application = await this.load() | ||
67 | |||
68 | return application.nodeABIVersion !== getNodeABIVersion() | ||
69 | } | ||
70 | |||
71 | static async updateNodeVersions () { | ||
72 | const application = await this.load() | ||
73 | |||
74 | application.nodeABIVersion = getNodeABIVersion() | ||
75 | application.nodeVersion = process.version | ||
76 | } | ||
55 | } | 77 | } |
diff --git a/server/tests/api/server/plugins.ts b/server/tests/api/server/plugins.ts index 8aa34fb15..3ae99dc2e 100644 --- a/server/tests/api/server/plugins.ts +++ b/server/tests/api/server/plugins.ts | |||
@@ -2,6 +2,8 @@ | |||
2 | 2 | ||
3 | import 'mocha' | 3 | import 'mocha' |
4 | import * as chai from 'chai' | 4 | import * as chai from 'chai' |
5 | import { pathExists, remove } from 'fs-extra' | ||
6 | import { join } from 'path' | ||
5 | import { testHelloWorldRegisteredSettings } from '@server/tests/shared' | 7 | import { testHelloWorldRegisteredSettings } from '@server/tests/shared' |
6 | import { wait } from '@shared/core-utils' | 8 | import { wait } from '@shared/core-utils' |
7 | import { HttpStatusCode, PluginType } from '@shared/models' | 9 | import { HttpStatusCode, PluginType } from '@shared/models' |
@@ -9,6 +11,7 @@ import { | |||
9 | cleanupTests, | 11 | cleanupTests, |
10 | createSingleServer, | 12 | createSingleServer, |
11 | killallServers, | 13 | killallServers, |
14 | makeGetRequest, | ||
12 | PeerTubeServer, | 15 | PeerTubeServer, |
13 | PluginsCommand, | 16 | PluginsCommand, |
14 | setAccessTokensToServers | 17 | setAccessTokensToServers |
@@ -349,6 +352,35 @@ describe('Test plugins', function () { | |||
349 | await check() | 352 | await check() |
350 | }) | 353 | }) |
351 | 354 | ||
355 | it('Should rebuild native modules on Node ABI change', async function () { | ||
356 | await command.install({ path: PluginsCommand.getPluginTestPath('-native') }) | ||
357 | |||
358 | await makeGetRequest({ | ||
359 | url: server.url, | ||
360 | path: '/plugins/test-native/router', | ||
361 | expectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
362 | }) | ||
363 | |||
364 | const query = `UPDATE "application" SET "nodeABIVersion" = 1` | ||
365 | await server.sql.updateQuery(query) | ||
366 | |||
367 | const baseNativeModule = server.servers.buildDirectory(join('plugins', 'node_modules', 'a-native-example')) | ||
368 | await remove(join(baseNativeModule, 'build')) | ||
369 | await remove(join(baseNativeModule, 'prebuilds')) | ||
370 | |||
371 | await server.kill() | ||
372 | await server.run() | ||
373 | |||
374 | await pathExists(join(baseNativeModule, 'build')) | ||
375 | await pathExists(join(baseNativeModule, 'prebuilds')) | ||
376 | |||
377 | await makeGetRequest({ | ||
378 | url: server.url, | ||
379 | path: '/plugins/test-native/router', | ||
380 | expectedStatus: HttpStatusCode.NO_CONTENT_204 | ||
381 | }) | ||
382 | }) | ||
383 | |||
352 | after(async function () { | 384 | after(async function () { |
353 | await cleanupTests([ server ]) | 385 | await cleanupTests([ server ]) |
354 | }) | 386 | }) |
diff --git a/server/tests/fixtures/peertube-plugin-test-native/main.js b/server/tests/fixtures/peertube-plugin-test-native/main.js new file mode 100644 index 000000000..0390faea9 --- /dev/null +++ b/server/tests/fixtures/peertube-plugin-test-native/main.js | |||
@@ -0,0 +1,21 @@ | |||
1 | const print = require('a-native-example') | ||
2 | |||
3 | async function register ({ getRouter }) { | ||
4 | print('hello world') | ||
5 | |||
6 | const router = getRouter() | ||
7 | |||
8 | router.get('/', (req, res) => { | ||
9 | print('hello world') | ||
10 | res.sendStatus(204) | ||
11 | }) | ||
12 | } | ||
13 | |||
14 | async function unregister () { | ||
15 | return | ||
16 | } | ||
17 | |||
18 | module.exports = { | ||
19 | register, | ||
20 | unregister | ||
21 | } | ||
diff --git a/server/tests/fixtures/peertube-plugin-test-native/package.json b/server/tests/fixtures/peertube-plugin-test-native/package.json new file mode 100644 index 000000000..a6525720b --- /dev/null +++ b/server/tests/fixtures/peertube-plugin-test-native/package.json | |||
@@ -0,0 +1,23 @@ | |||
1 | { | ||
2 | "name": "peertube-plugin-test-native", | ||
3 | "version": "0.0.1", | ||
4 | "description": "Plugin test-native", | ||
5 | "engine": { | ||
6 | "peertube": ">=4.3.0" | ||
7 | }, | ||
8 | "keywords": [ | ||
9 | "peertube", | ||
10 | "plugin" | ||
11 | ], | ||
12 | "homepage": "https://github.com/Chocobozzz/PeerTube", | ||
13 | "author": "Chocobozzz", | ||
14 | "bugs": "https://github.com/Chocobozzz/PeerTube/issues", | ||
15 | "library": "./main.js", | ||
16 | "staticDirs": {}, | ||
17 | "css": [], | ||
18 | "clientScripts": [], | ||
19 | "translations": {}, | ||
20 | "dependencies": { | ||
21 | "a-native-example": "^1.0.0" | ||
22 | } | ||
23 | } | ||