-
-
Notifications
You must be signed in to change notification settings - Fork 5.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Desktop: Bundle default plugins with desktop application #6679
Changes from 10 commits
0c95341
7e0aeae
4e04b9e
b926ee6
e6ded17
68a611f
427da27
a327f8a
4cf0d00
267e5ed
3d4bfd2
cc59c90
725e003
e87edf5
25f636d
c77c180
8c692f8
e46c305
ddf69ae
15a3dae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { supportDir } from '@joplin/lib/testing/test-utils'; | ||
import path = require('path'); | ||
const fs = require('fs'); | ||
|
||
const response1 = { | ||
'_id': 'joplin-plugin-rich-markdown', | ||
'name': 'joplin-plugin-rich-markdown', | ||
'versions': { | ||
'0.8.2': { | ||
'name': 'joplin-plugin-rich-markdown', | ||
'version': '0.8.2', | ||
'description': 'A plugin that will finally allow you to ditch the markdown viewer, saving space and making your life easier.', | ||
'_id': '[email protected]', | ||
'dist': { | ||
'tarball': 'no-link-here', | ||
}, | ||
}, | ||
'0.9.0': { | ||
'name': 'joplin-plugin-rich-markdown', | ||
'version': '0.9.0', | ||
'dist': { | ||
'tarball': `${path.join(supportDir, '..', 'services', 'plugins', 'mockData', 'richMarkdown.tgz')}`, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
const response2 = { | ||
'_id': 'io.github.jackgruber.backup', | ||
'name': 'joplin-plugin-rich-markdown', | ||
'versions': { | ||
'1.0.0': { | ||
'name': 'joplin-plugin-rich-markdown', | ||
'version': '1.0.0', | ||
'description': 'A plugin that will finally allow you to ditch the markdown viewer, saving space and making your life easier.', | ||
'_id': '[email protected]', | ||
'dist': { | ||
'tarball': 'no-link-here', | ||
}, | ||
}, | ||
'1.1.0': { | ||
'name': 'joplin-plugin-rich-markdown', | ||
'version': '1.1.0', | ||
'dist': { | ||
'tarball': `${path.join(supportDir, '..', 'services', 'plugins', 'mockData', 'simpleBackup.tgz')}`, | ||
}, | ||
}, | ||
}, | ||
}; | ||
|
||
export const manifests = { | ||
'io.github.jackgruber.backup': { | ||
'manifest_version': 1, | ||
'id': 'io.github.jackgruber.backup', | ||
'app_min_version': '2.1.3', | ||
'version': '1.1.0', | ||
'name': 'Simple Backup', | ||
'description': 'Plugin to create manual and automatic backups.', | ||
'author': 'JackGruber', | ||
'homepage_url': 'https://github.com/JackGruber/joplin-plugin-backup/blob/master/README.md', | ||
'repository_url': 'https://github.com/JackGruber/joplin-plugin-backup', | ||
'keywords': [ | ||
'backup', | ||
'jex', | ||
'export', | ||
'zip', | ||
'7zip', | ||
'encrypted', | ||
], | ||
'_publish_hash': 'sha256:8d8c6a3bb92fafc587269aea58b623b05242d42c0766a05bbe25c3ba2bbdf8ee', | ||
'_publish_commit': 'master:00ed52133c659e0f3ac1a55f70b776c42fca0a6d', | ||
'_npm_package_name': 'joplin-plugin-backup', | ||
}, | ||
'plugin.calebjohn.rich-markdown': { | ||
'manifest_version': 1, | ||
'id': 'plugin.calebjohn.rich-markdown', | ||
'app_min_version': '2.7', | ||
'version': '0.9.0', | ||
'name': 'Rich Markdown', | ||
'description': 'Helping you ditch the markdown viewer for good.', | ||
'author': 'Caleb John', | ||
'homepage_url': 'https://github.com/CalebJohn/joplin-rich-markdown#readme', | ||
'repository_url': 'https://github.com/CalebJohn/joplin-rich-markdown', | ||
'keywords': [ | ||
'editor', | ||
'visual', | ||
], | ||
'_publish_hash': 'sha256:95337a3868aebdc9bf8c347a37460d0c2753b391ff51a0c72bdccdef9679705f', | ||
'_publish_commit': 'main:af3493b6ca96c931327ab3bd04906faaed0c782c', | ||
'_npm_package_name': 'joplin-plugin-rich-markdown', | ||
}, | ||
|
||
}; | ||
|
||
export async function mockFile() { | ||
const filePath = path.join(__dirname, 'richMarkdown.tgz'); | ||
const someFile = await fs.readFileSync(filePath, 'utf8'); | ||
return { buffer: () => someFile }; | ||
} | ||
|
||
export const NPM_Response1 = JSON.stringify(response1); | ||
export const NPM_Response2 = JSON.stringify(response2); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
{ | ||
"manifest_version": 1, | ||
"id": "io.github.jackgruber.backup", | ||
"app_min_version": "2.1.3", | ||
"version": "1.1.0", | ||
"name": "Simple Backup", | ||
"description": "Plugin to create manual and automatic backups.", | ||
"author": "JackGruber", | ||
"homepage_url": "https://github.com/JackGruber/joplin-plugin-backup/blob/master/README.md", | ||
"repository_url": "https://github.com/JackGruber/joplin-plugin-backup", | ||
"keywords": [ | ||
"backup", | ||
"jex", | ||
"export", | ||
"zip", | ||
"7zip", | ||
"encrypted" | ||
], | ||
"_publish_hash": "sha256:8d8c6a3bb92fafc587269aea58b623b05242d42c0766a05bbe25c3ba2bbdf8ee", | ||
"_publish_commit": "master:00ed52133c659e0f3ac1a55f70b776c42fca0a6d", | ||
"_npm_package_name": "joplin-plugin-backup" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"manifest_version": 1, | ||
"id": "plugin.calebjohn.rich-markdown", | ||
"app_min_version": "2.7", | ||
"version": "0.9.0", | ||
"name": "Rich Markdown", | ||
"description": "Helping you ditch the markdown viewer for good.", | ||
"author": "Caleb John", | ||
"homepage_url": "https://github.com/CalebJohn/joplin-rich-markdown#readme", | ||
"repository_url": "https://github.com/CalebJohn/joplin-rich-markdown", | ||
"keywords": [ | ||
"editor", | ||
"visual" | ||
], | ||
"_publish_hash": "sha256:95337a3868aebdc9bf8c347a37460d0c2753b391ff51a0c72bdccdef9679705f", | ||
"_publish_commit": "main:af3493b6ca96c931327ab3bd04906faaed0c782c", | ||
"_npm_package_name": "joplin-plugin-rich-markdown" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
|
||
|
||
|
||
import path = require('path'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only import the functions you need |
||
import { downloadPlugins, localPluginsVersion } from './bundleDefaultPlugins'; | ||
import fs = require('fs-extra'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't import There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Noted 👍 |
||
import { mockFile, NPM_Response1 } from '../app-cli/tests/services/plugins/mockData/mockResponses'; | ||
// import { supportDir } from '@joplin/lib/testing/test-utils'; | ||
import { manifests } from '../app-cli/tests/services/plugins/mockData/mockResponses'; | ||
// const {Response} = jest.requireActual('node-fetch'); | ||
|
||
const fetch = require('node-fetch'); | ||
|
||
jest.mock('node-fetch', ()=>jest.fn()); | ||
|
||
describe('bundleDefaultPlugins', function() { | ||
|
||
beforeEach(() => { | ||
jest.setTimeout(20000); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this setTimeout for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am using this just to increase the timeout for tests on my local system, otherwise they seem to fail randomly. This will be removed before merging the code. |
||
}); | ||
|
||
const testDefaultPluginsIds = { | ||
'plugin.calebjohn.rich-markdown': '0.9.0', | ||
'io.github.jackgruber.backup': '1.1.0', | ||
}; | ||
|
||
|
||
// test('it should get local plugin versions', async () => { | ||
// const manifestsPath = path.join(__dirname, '..', '..', '..' , 'joplin/packages/app-cli/tests/services/testPlugins'); | ||
|
||
// const localPluginsVersions = await localPluginsVersion(manifestsPath, testDefaultPluginsIds); | ||
|
||
// expect(localPluginsVersions['io.github.jackgruber.backup']).toBe('1.1.0'); | ||
// expect(localPluginsVersions['plugin.calebjohn.rich-markdown']).toBe('0.9.0'); | ||
// }); | ||
|
||
it('it should download plugins folder from GitHub with no initial plugins', async () => { | ||
|
||
const mockTGZ = await mockFile(); | ||
const mockFetch = fetch as jest.MockedFunction<typeof fetch>; | ||
mockFetch.mockResolvedValueOnce({ text: () => Promise.resolve(NPM_Response1), ok: true }) | ||
.mockResolvedValueOnce({ ...mockTGZ, ok: true }); | ||
|
||
const tempDir = path.join(__dirname, '/tempDownload'); | ||
|
||
const localPluginsVersions = { 'io.github.jackgruber.backup': '0.0.0', 'plugin.calebjohn.rich-markdown': '0.0.0' }; | ||
|
||
await fs.mkdirp(`${tempDir}`); | ||
await downloadPlugins(tempDir, localPluginsVersions, testDefaultPluginsIds, manifests); | ||
|
||
expect(fs.existsSync(`${tempDir}/io.github.jackgruber.backup`)).toBe(true); | ||
expect(fs.existsSync(`${tempDir}/plugin.calebjohn.rich-markdown`)).toBe(true); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Only use async methods |
||
|
||
const localPluginsVersions2 = await localPluginsVersion(tempDir, testDefaultPluginsIds); | ||
|
||
expect(localPluginsVersions2['plugin.calebjohn.rich-markdown']).toBe('0.9.0'); | ||
expect(localPluginsVersions2['io.github.jackgruber.backup']).toBe('1.1.0'); | ||
|
||
await fs.remove(tempDir); | ||
}); | ||
|
||
// test('it should download plugins folder from GitHub with initial plugins', async () => { | ||
|
||
// const tempDir = path.join(__dirname, '/tempDownload'); | ||
// await fs.remove(tempDir); | ||
|
||
// let manifests = []; | ||
// try { | ||
// const manifestData = await fetch('https://raw.githubusercontent.com/joplin/plugins/master/manifests.json'); | ||
// manifests = JSON.parse(await manifestData.text()); | ||
// if (!manifests) throw new Error('Invalid or missing JSON'); | ||
// } catch (error) { | ||
// console.log(error); | ||
// } | ||
|
||
// const localPluginsVersions = { 'io.github.jackgruber.backup': '1.1.0', 'plugin.calebjohn.rich-markdown': '0.0.0' }; | ||
|
||
// await fs.mkdirp(`${tempDir}`); | ||
// await downloadPlugins(tempDir, localPluginsVersions, testDefaultPluginsIds, manifests); | ||
|
||
// expect(fs.existsSync(`${tempDir}/io.github.jackgruber.backup`)).toBe(false); | ||
// expect(fs.existsSync(`${tempDir}/plugin.calebjohn.rich-markdown`)).toBe(true); | ||
|
||
// await fs.remove(tempDir); | ||
// }); | ||
|
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import path = require('path'); | ||
import { execCommand2 } from './tool-utils'; | ||
import fs = require('fs-extra'); | ||
import { defaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/desktopDefaultPluginsInfo'; | ||
const fetch = require('node-fetch'); | ||
const { writeFile } = require('fs'); | ||
const { promisify } = require('util'); | ||
const writeFilePromise = promisify(writeFile); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just import from fs-extra, it's already promisified |
||
// const util = require('util'); | ||
|
||
interface PluginAndVersion { | ||
[pluginId: string]: string; | ||
} | ||
|
||
export const localPluginsVersion = async (defaultPluginDir: string, defaultPluginsId: PluginAndVersion): Promise<PluginAndVersion> => { | ||
if (!await fs.pathExists(path.join(defaultPluginDir))) await fs.mkdir(defaultPluginDir); | ||
const localPluginsVersions: PluginAndVersion = {}; | ||
|
||
for (const pluginId of Object.keys(defaultPluginsId)) { | ||
|
||
if (!await fs.pathExists(path.join(__dirname, '..', '..', `packages/app-desktop/build/defaultPlugins/${pluginId}`))) { | ||
localPluginsVersions[pluginId] = '0.0.0'; | ||
continue; | ||
} | ||
const data = fs.readFileSync(`${defaultPluginDir}/${pluginId}/manifest.json`, 'utf8'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only async calls |
||
const manifest = JSON.parse(data); | ||
localPluginsVersions[pluginId] = manifest.version; | ||
} | ||
return localPluginsVersions; | ||
}; | ||
|
||
function downloadFile(url: string, outputPath: string) { | ||
return fetch(url).then(response => response.buffer()).then(buff => writeFilePromise(outputPath, Buffer.from(buff))); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no then, only await/async |
||
} | ||
|
||
export const downloadPlugins = async (defaultPluginDir: string, localPluginsVersions: PluginAndVersion, defaultPluginsId: PluginAndVersion, manifests: any): Promise<void> => { | ||
|
||
for (const pluginId of Object.keys(defaultPluginsId)) { | ||
if (localPluginsVersions[pluginId] === defaultPluginsId[pluginId]) continue; | ||
const response = await fetch(`https://registry.npmjs.org/${manifests[pluginId]._npm_package_name}`); | ||
|
||
if (!(response.ok)) { | ||
// const responseText = await response.text(); | ||
throw new Error('Cannot get latest release info'); | ||
} | ||
const release = JSON.parse(await response.text()); | ||
|
||
const pluginUrl = release.versions[defaultPluginsId[pluginId]].dist.tarball; | ||
|
||
const pluginName = `${manifests[pluginId]._npm_package_name}-${defaultPluginsId[pluginId]}.tgz`; | ||
await downloadFile(pluginUrl, pluginName); | ||
|
||
if (!fs.existsSync(pluginName)) throw new Error(`${pluginName} cannot be downloaded`); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. only async code |
||
|
||
await execCommand2(`tar xvzf ${pluginName}`); | ||
await fs.move(`package/publish/${pluginId}.jpl`,`${defaultPluginDir}/${pluginId}/plugin.jpl`, { overwrite: true }); | ||
await fs.move(`package/publish/${pluginId}.json`,`${defaultPluginDir}/${pluginId}/manifest.json`, { overwrite: true }); | ||
await fs.remove(`${pluginName}`); | ||
await fs.remove('package'); | ||
} | ||
}; | ||
|
||
async function start(): Promise<void> { | ||
const defaultPluginDir = path.join(__dirname, '..', '..', 'packages/app-desktop/build/defaultPlugins'); | ||
|
||
const manifestData = await fetch('https://raw.githubusercontent.com/joplin/plugins/master/manifests.json'); | ||
const manifests = JSON.parse(await manifestData.text()); | ||
if (!manifests) throw new Error('Invalid or missing JSON'); | ||
|
||
const localPluginsVersions = await localPluginsVersion(defaultPluginDir, defaultPlugins); | ||
await downloadPlugins(defaultPluginDir, localPluginsVersions, defaultPlugins, manifests); | ||
} | ||
|
||
if (require.main === module) { | ||
start().catch((error) => { | ||
console.error('Fatal error'); | ||
console.error(error); | ||
process.exit(1); | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Line 175 Is meant to be run form the packages/app-desktop directory, but this line places the script in packages/tools. This appears like it will break the ci build (for releases).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. I will fix it.