This is the companion to cgkineo/adapt-authoring-adaptframework-extras#9.
The adapt-cli PR #238 adds support for installing plugins directly from HTTPS git URLs (e.g. `adapt install https://github.com/org/plugin.git#v2.0.0\`). The contentplugin module needs to support this path so that:
- Plugins can be installed/updated by passing a git URL through the authoring tool API
- Git-installed plugins are tracked correctly in the DB and can be reinstalled after a restart
Implementation
Prerequisite: adapt-cli PR #238 must be merged and released.
1. DB schema — `schema/contentplugin.schema.json`
Add an optional `gitUrl` property to persist the source URL for git-installed plugins (analogous to `isLocalInstall`):
"gitUrl": {
"description": "The HTTPS git URL this plugin was installed from, if applicable",
"type": "string"
}
2. `installPlugin` — `lib/ContentPluginModule.js`
The current flow passes `versionOrPath` to `processPluginFiles`, which assumes a semver string or a local file path. A git URL will cause `fs.readdir` to throw.
Add a git URL detection branch before calling `processPluginFiles`:
async installPlugin (pluginName, versionOrPath, options = {}) {
const isGitUrl = /^https?:\/\//.test(versionOrPath)
let name, version, sourcePath, isLocalInstall, gitUrl
if (isGitUrl) {
// versionOrPath is e.g. "https://github.com/org/plugin.git#v2.0.0"
// Pass it directly to the CLI — adapt-cli PR #238 handles git URL installs
name = pluginName
sourcePath = null
isLocalInstall = false
gitUrl = versionOrPath.split('#')[0]
} else {
const pluginData = await this.findOne({ name: String(pluginName) }, { includeUpdateInfo: true, strict: false })
;({ name, version, sourcePath, isLocalInstall } = await this.processPluginFiles({ ...pluginData, sourcePath: versionOrPath }))
}
const existingPlugin = await this.findOne({ name }, { strict: false })
if (existingPlugin && !options.force) {
if (!isGitUrl && semver.lte(version, existingPlugin.version)) {
throw this.app.errors.CONTENTPLUGIN_ALREADY_EXISTS.setData({ name: existingPlugin.name, version: existingPlugin.version })
}
}
const pluginArg = isGitUrl ? versionOrPath : `${name}@${sourcePath ?? version}`
const [data] = await this.framework.runCliCommand('installPlugins', { plugins: [pluginArg] })
const dbData = { ...(await data.getInfo()), type: await data.getType(), isLocalInstall }
if (isGitUrl) dbData.gitUrl = gitUrl
const info = await this.insertOrUpdate(dbData)
if (!data.isInstallSuccessful) {
throw this.app.errors.CONTENTPLUGIN_CLI_INSTALL_FAILED.setData({ name })
}
if (!info.targetAttribute) {
throw this.app.errors.CONTENTPLUGIN_ATTR_MISSING.setData({ name })
}
await this.processPluginSchemas(data)
return info
}
Note: after the CLI installs a git plugin, `data.name` is resolved from the cloned `bower.json` — so even if `pluginName` was an empty string it will be populated. Ensure `data.getInfo()` is called after `data.name` is set (adapt-cli PR #238 does this correctly via `fetchGitSourceInfo`).
3. `getMissingPlugins` — `lib/ContentPluginModule.js`
Currently non-local plugins are reinstalled as `${name}@${version}`. Git-installed plugins must be reinstalled using their stored git URL instead:
// For non-local, non-git installs:
return `${p.name}@${p.version}`
// For git installs (new):
if (p.gitUrl) return p.gitUrl
return `${p.name}@${p.version}`
Replace the existing non-local branch with this check.
4. `syncPluginData` — `lib/ContentPluginModule.js`
After adapt-cli PR #238, `getPluginUpdateInfos()` returns Plugin objects with `gitUrl` set for git-installed plugins (read from `_gitUrl` in `.bower.json`). Persist this when syncing:
for (const i of await this.framework.runCliCommand('getPluginUpdateInfos')) {
if (dbInfo[i.name]?.version !== i.matchedVersion) {
const pluginInfo = {
...(await i.getInfo()),
type: await i.getType(),
isLocalInstall: i.isLocalSource
}
if (i.isGitSource) pluginInfo.gitUrl = i.gitUrl
await this.insertOrUpdate(pluginInfo)
}
}
5. `updatePlugin` — `lib/ContentPluginModule.js`
The CLI's `updatePlugins` command already re-clones git plugins from their stored URL (adapt-cli PR #238). No special branching is needed here, but ensure that after update the `gitUrl` is preserved in the DB. Since `syncPluginData` is called via `postUpdateHook` already, this should be handled by the fix in step 4.
6. `installHandler` — `lib/ContentPluginModule.js`
The existing handler reads `req.body.version` as the version/path. No changes needed — a git URL submitted as `version` will be handled by the updated `installPlugin` method above.
Tests
- `tests/utils-processPluginFiles.spec.js` (if it exists) or inline: confirm that a git URL input does not reach `fs.readdir`
- New spec or additions to `ContentPluginUtils.spec.js`: mock `framework.runCliCommand` and verify that `installPlugin` with a git URL passes the full URL string to `installPlugins`, stores `gitUrl` in the DB record, and skips the `semver.lte` guard
- Cover `getMissingPlugins`: when a DB plugin has `gitUrl` set, the returned reinstall spec is the git URL, not `name@version`
This is the companion to cgkineo/adapt-authoring-adaptframework-extras#9.
The adapt-cli PR #238 adds support for installing plugins directly from HTTPS git URLs (e.g. `adapt install https://github.com/org/plugin.git#v2.0.0\`). The contentplugin module needs to support this path so that:
Implementation
1. DB schema — `schema/contentplugin.schema.json`
Add an optional `gitUrl` property to persist the source URL for git-installed plugins (analogous to `isLocalInstall`):
2. `installPlugin` — `lib/ContentPluginModule.js`
The current flow passes `versionOrPath` to `processPluginFiles`, which assumes a semver string or a local file path. A git URL will cause `fs.readdir` to throw.
Add a git URL detection branch before calling `processPluginFiles`:
3. `getMissingPlugins` — `lib/ContentPluginModule.js`
Currently non-local plugins are reinstalled as `${name}@${version}`. Git-installed plugins must be reinstalled using their stored git URL instead:
Replace the existing non-local branch with this check.
4. `syncPluginData` — `lib/ContentPluginModule.js`
After adapt-cli PR #238, `getPluginUpdateInfos()` returns Plugin objects with `gitUrl` set for git-installed plugins (read from `_gitUrl` in `.bower.json`). Persist this when syncing:
5. `updatePlugin` — `lib/ContentPluginModule.js`
The CLI's `updatePlugins` command already re-clones git plugins from their stored URL (adapt-cli PR #238). No special branching is needed here, but ensure that after update the `gitUrl` is preserved in the DB. Since `syncPluginData` is called via `postUpdateHook` already, this should be handled by the fix in step 4.
6. `installHandler` — `lib/ContentPluginModule.js`
The existing handler reads `req.body.version` as the version/path. No changes needed — a git URL submitted as `version` will be handled by the updated `installPlugin` method above.
Tests