{"id":20811726,"url":"https://github.com/strider-cd/strider-extension-loader","last_synced_at":"2025-05-07T10:09:55.317Z","repository":{"id":4148818,"uuid":"5262829","full_name":"Strider-CD/strider-extension-loader","owner":"Strider-CD","description":"Loads Strider extensions","archived":false,"fork":false,"pushed_at":"2023-03-04T02:47:42.000Z","size":709,"stargazers_count":21,"open_issues_count":5,"forks_count":11,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-10-31T14:52:54.383Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"sendgrid/sendgrid-php","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Strider-CD.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2012-08-01T18:13:10.000Z","updated_at":"2022-01-15T15:47:12.000Z","dependencies_parsed_at":"2023-07-06T07:27:46.660Z","dependency_job_id":null,"html_url":"https://github.com/Strider-CD/strider-extension-loader","commit_stats":{"total_commits":139,"total_committers":13,"mean_commits":"10.692307692307692","dds":0.7338129496402878,"last_synced_commit":"5f9b1918be904acf4abb68706bc23b8b764abac5"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Strider-CD%2Fstrider-extension-loader","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Strider-CD%2Fstrider-extension-loader/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Strider-CD%2Fstrider-extension-loader/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Strider-CD%2Fstrider-extension-loader/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Strider-CD","download_url":"https://codeload.github.com/Strider-CD/strider-extension-loader/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225089237,"owners_count":17419280,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-17T20:46:48.963Z","updated_at":"2024-11-17T20:46:49.670Z","avatar_url":"https://github.com/Strider-CD.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Strider Extension Loader\n\n[Strider](https://github.com/Strider-CD/strider) is an extensible CI\nsystem, written in node. Strider extensions are simply NPM packages\nwith additional metadata contained in a file named\n`strider.json`. This metadata tells Strider which JavaScript source\nfiles should be loaded and initialized.\n\nHence, to install a new Strider extension, you can just `npm install`\nit in your strider repositiory.\n\nThis is a small Node.JS library for loading Strider extensions.\n\n## API\n\n```js\nvar Loader = require('strider-extension-loader');\n\nvar loader = new Loader();\n```\n\n### `new Loader(lesspaths, isNamespaced)`\n* `lesspaths` is an optional list of directories that will be made\n  available while compiling plugins' `less` style files.\n* `isNamespaced` is for backwards compatibility with older versions\n  where the default type controllers e.g. `JobController` were not namespaced.\n  For versions \u003c 1.6.0 this property should NOT be set.\n\n### `.collectExtensions(dirs, done(err))`\n\nCollect all strider extensions found in the given directories.\n\n### `.initWebAppExtensions(context, done(err, extensions))`\n\nLoad the \"webapp\" portion of all extensions.\n\n`extensions` looks like `{ plugintype: { pluginid: loadedPlugin, ... }, ... }`\n\nThe structure of the `loadedPlugin` object depend on the plugin type.\n- job: \n\n### `.initWorkerExtensions(context, done(err, extensions))`\n\nSame as `initWebAppExtensions` but for the `worker` portion.\n\n### `.initTemplates(done(err, templates))`\n\nLoad all of the templates from all extensions. `templates` looks like\n`{ templatename: stringtemplate, ... }`.\n\n### `.initStaticDirs(app, done(err))`\n\nRegister the `/static/` directories of all plugins to map to `/ext/:pluginid`.\n\n### `.initConfig(jspath, csspath, done(err, configs))`\n\nAssets for configuring plugins on the `/my/project/config` page.\n\nCollect the html, js, and css for all plugins. This is per-project\nconfig. js scripts will each be wrapped in an anonymous function to\nnamespace it, and concatenated into one file (in future it will also\nbe minified). Stylesheets will also be concatenated together, and\n`.less` files will be compiled to css (with the lesspaths available\nfor imports).\n\nThen the js and css are written to the files specified `jspath` and\n`csspath`.\n\nHtml for the templates are available on the `configs` objects.\n\nConfigs look like `{ plugintype: { pluginid: config, ...}, ... }` and\n`config` looks like:\n\n```js\n{\n  id: \"myplugin\",\n  controller: \"MyController\", // defaults to [plugintype]Controller\n  html: \"\u003cthe\u003ehtml\u003c/the\u003e\",    // loaded from config.template\n  icon: \"icon.png\",           // relative to the plugin's `static` directory\n  title: \"My Plugin\"\n}\n```\n\nBasically, this is constructed from the `strider` section of\npackage.json.\n\n```js\n\"strider\": {\n  \"id\": \"myplugin\",\n  \"title\": \"My Plugin\",\n  \"icon\": \"icon.png\", // should be in the ./static dir\n  \"config\": {\n    \"controller\": // defaults to \"Config.JobController\" for job plugins, \"Config.ProviderController\", etc.\n    \"script\":     // path where the js should be loaded from. Path defaults to \"config/config.js\"\n    \"style\":      // defaults to \"config/config.less\"\n    \"template\":   // defaults to \"config/config.html\"\n  }\n}\n```\n\nI hope that's clear.\n\n#### Angular Controller\n\nIf you don't need to do anything fancy, you can just use the default\ncontroller for your plugin type. Take a look in\n[strider's `client/config/controllers` directory][config-controllers] for the source of\nthose controllers. Basically, each controller makes available a\n`config` object on the scope, which is populated by the plugin's\nconfig for the currently selected branch. Also a `save()` function is\navailable on the scope.\n\nSo, for example, the simplest configuration template for any plugin\ntype could just have\n\n```html\n\u003cinput ng-model=\"config.oneAttr\" placeholder=\"One Attribute Here\"\u003e\n\u003cbutton class=\"btn\" ng-click=\"save()\"\u003eSave\u003c/button\u003e\n```\n\nNo javascript required. Just put that in \"config/config.html\" and\nyou're done.\n\n### `.initUserConfig(jspath, csspath, done)`\n\nThis is very similar to initConfig, but for per-user as opposed to\nper-project config. For provider plugins, the default file name is\n`config/accountConfig.html, js, less`, and for all other plugin types\nit's `config/userConfig.html, js, less`.\n\n### `.initStatusBlocks(jspath, csspath, done)`\n\nStatus blocks allow plugins to \n\n## Strider Extensions\n\n### Extension types\n\n- **runner:** runs the jobs, like strider-simple-runner\n- **provider:** gets the source code for a project, like strider-github,\n  strider-bitbucket or strider-git. Can be hosted or regular.\n- **job:** setup the environment, run tests, deploy etc. like strider-node or strider-sauce\n- **basic:** do whatever you want. If you need more power, use this, but\n  you don't get the helpers provided by the more specific plugin types.\n\n### Webapp vs Worker\n\nThere are two environments where plugins are loaded, webapp and worker.\n\n#### Webapp environment\n\nEffects the way the strider webapp works, how it looks, etc. You can\ndefine templates, serve static files, listen to global strider events,\nand other great things.\n\n#### Worker environment\n\nThis code is loaded for each job that is run, by the process that is\nrunning the job. This may be the same process as the webapp (as when\nusing `strider-simple-runner`), or it might be somewhere else\nentirely. Accordingly, it is recommended that you not depend on\nnetwork connections unless absolutely necessary. In many cases, you\ncan pass a message up to the strider app and handle it in your\n`webapp` code.\n\n### Strider.json\n\nTo declare your npm package as a strider plugin, include a\n`strider.json` in the base directory. Alternatively, you can have a\n`strider` section to your `package.json`.\n\n`strider.json` schema:\n\n```javascript\n{\n  \"id\": \"pluginid\", // must be unique.\n  \"title\": \"Human Readable\",\n  \"icon\": \"icon.png\", // relative to the plugin's `static` directory\n  \"type\": \"runner | provider | job | basic\", // defaults to basic\n  \"webapp\": \"filename.js\", // loaded in the webapp environment\n  \"worker\": \"filename.js\", // loaded in the worker environment\n  \"templates\": {\n    \"tplname\": \"\u003cdiv\u003eHello {{ name }}\u003c/div\u003e\",\n    \"tplname\": \"path/to/tpl.html\"\n  },\n  \"config\": { // project-specific configuration\n    \"controller\": // defaults to \"Config.JobController\" for job plugins, \"Config.ProviderController\", etc.\n    \"script\":     // path where the js should be loaded from. Path defaults to \"config/config.js\"\n    \"style\":      // defaults to \"config/config.less\". Can be less or css\n    \"template\":   // defaults to \"config/config.html\"\n  }\n  // other configurations\n}\n```\n\nAdditionally, if there is a `/static/` directory, the files within it\nwill be accessible at the path `/ext/:pluginid`.\n\n### Runner\n\nRunner plugins do not get loaded in the worker environment.\n\n#### Webapp\n\n```javascript\nmodule.exports = {\n  config: {}, // mongoose schema. This will be per-project config\n  appConfig: {}, // mongoose schema. Global config\n  create: function (emitter, options, callback(err, runner)) {}\n};\n```\n\n#### Runner object\n\n##### properties\n\nThese are used for the strider admin dashboard.\n\n- `capacity`\n- `running` number of jobs currently running\n- `queued` length of the queue\n\n##### handles events\n\n- `job.new (job)` see strider-runner-core for a description of the `job` data\n- `job.cancel (jobid)` if the runner has the specified job, either\n  queued or in process, stop it and fire the `job.canceled` event\n\nRunners are only expected to handle a job if `job.project.runner.id`\nidentifies it as belonging to this runner.\n\n##### emits events\n\n- `browser.update (eventname, data)` this is for proxying internal\n  `job.status` events up to the browser\n- `job.queued (jobid, time)`\n- `job.done (data)`\n\n#### Extra config\n\n- `panel` see the job plugin section\n- `appPanel` similar to panel, but for global config\n\n### Provider\n\nProvider plugins that need an ssh keypair are encouraged to use the\n`privkey` and `pubkey` that are defined for each project. They are\nattributes on the `project` object.\n\n#### Webapp\n\n```javascript\nmodule.exports = {\n  // mongoose schema for project-specific config\n  config: {},\n \n  // mongoose schema for user-level config (like a github OAuth token) and/or cache\n  userConfig: {},\n  // optional; used by services such as github, bitbucket, etc.\n \n  // getFile: used to get the .strider.json file\n  getFile: function (filepath, userConfig, config, project, done(err, filecontents))\n \n  // getBranches: get the branches for a repository\n  getBranches: function (userConfig, config, project, done(err, [branchname, ...]))\n \n  // listRepos: only used by providers that connect to a hosted service.\n  // repos: { groupname: [repo, ...], groupname: ... }\n  listRepos: function (userConfig, done(err, repos)) {},\n\n  // if this provider plugin needs setup (in github's case, oauth) this string\n  // represents the href link to the page to handle that.\n  setupLink: \"/ext/github/oauth\",\n\n  // determine whether or not this provider is setup for this user.\n  // e.g. for github, that we have an oauth key\n  // returns boolean\n  isSetup: function (account) {},\n \n  initialize: function (userConfig, repo, done(err, name, display_url, config)) {},\n  // namespaced to /ext/:pluginid\n  routes: function (app, context) {}\n}\n```\n\nThe `repos` that are returned by `listRepos` contain objects\nwhich, when activated, will be the provider config for the project. As\nsuch, it is required to have a `url` that is unique, a `name` that\nlooks like \"org/name\" and it should also define a `display_url` where\nappropriate. All other config is up to you.\n\n```javascript\n{\n   name: 'some/name', // should have exactly one '/'\n   // this has to be unique; it's how we identify whether a project\n   // has already been configured.\n   url: 'http://example.com/unique/url.git',\n   // optional - linked to from the project page\n   display_url: 'http://example.com/path/to/repo'\n   // everything else is up to you.\n}\n```\n\n##### Worker\n\nIf just a function is exposed, it is assumed to be \"fetch\".\n\n```javascript\nmodule.exports = {\n  // get the source code for a project. This is where the real work gets done.\n  //   dest: the path to put things\n  //   context: contains runCmd, io (for event passing)\n  fetch: function (dest, userConfig, config, job, context, done) {}\n};\n```\n\n##### Extra Config\n\nUse `panel` for project-level config, and `userPanel` for user-level config.\n\n- `inline_icon` you can also define a `24x24` icon for the\n  `display_url` links. If this is not a path, it is assumed to be the\n  name of an icon from `FontAwesome` (without the `icon-` prefix) and\n  will be loaded as such. Defaults to `external-link`.\n\n### Job\n\n#### Webapp\n\n```javascript\n{\n   config: {}, // mongoose schema, if you need project-specific config\n   // Define project-specific routes\n   //   all routes created here are namespaced within /:org/:repo/api/:pluginid\n   //   req.project is the current project\n   //   req.accessLevel is the current user's access level for the project\n   //      0 - anonymous, 1 - authed, 2 - admin / collaborator\n   //   req.user is the current user\n   //   req.pluginConfig() -\u003e get the config for this plugin\n   //   req.pluginConfig(config, cb(err)) -\u003e set the config for this plugin\n   routes: function (app, context) {},\n   // Define global routes\n   //   all routes namespaced within /ext/:pluginid\n   //   req.user is the current user\n   //   req.user.account_level can be used for authorization\n   //      0 - anonymous, 1 - authed, 2 - admin / collaborator\n   globalRoutes: function (app, context) {},\n   // Listen for global events\n   //   all job-local events that begin with `plugin.` are proxied to\n   //   the main strider eventemitter, so you can listen for them here.\n   //   Other events include `job.new`, `job.done` and `browser.update`.\n   listen: function (emitter, context) {}\n}\n```\n\n#### Worker\n\nIf only a function is exposed, it is assumed to be the `init(config,\njob, cb)` function.\n\nAutodetection rules are only used when a project has no plugins\nconfigured.\n\n```javascript\nmodule.exports = {\n  // Initialize the plugin for a job\n  //   config: the config for this job, made by extending the DB config\n  //           with any flat-file config\n  //   job:    see strider-runner-core for a description of that object\n  //   context: currently only defines \"dataDir\"\n  //   cb(err, initializedPlugin)\n  init: function (config, job, context, cb) {\n    return cb(null, {\n      // string or list - to be added to the PATH\n      path: path.join(__dirname, 'bin'),\n      // any extra env variables. Will be available during all phases\n      env: {},\n      // Listen for events on the internal job emitter.\n      //   Look at strider-runner-core for an\n      //   enumeration of the events. Emit plugin.[pluginid].myevent to\n      //   communicate things up to the browser or to the webapp.\n      listen: function (emitter, context) {\n      },\n      // For each phase that you want to deal with, provide either a\n      // shell command [string] for a fn(context, done(err, didrun))\n      environment: 'nvm install ' + (config.version || '0.10'),\n      prepare: 'npm install',\n      test: function (context, done) {\n        checkSomething(context, function (shouldDoThings) {\n          if (!shouldDoThings) {\n            // Send `false` to indicate that we didn't actually run\n            // anything. This is so we can warn users when no plugins\n            // actually do anything during a test run, and avoid false\n            // positives.\n            return done(null, false);\n          }\n          doThings(function (err) {\n            done(err, true);\n          });\n        });\n      },\n      cleanup: 'rm -rf node_modules'\n    });\n  }\n  // this is only used if there is _no_ plugin configuration for a\n  // project. See gumshoe for documentation on detection rules.\n  autodetect: {\n    filename: 'package.json',\n    exists: true\n  }\n};\n```\n\n##### Phase Context\n\n- job\n- project\n- dataDir\n- phase\n\n###### `cmd(cmd || options, done(exitCode))` Run a shell command\n\n```js\n{\n  cmd: \"shell string\" || {command: \"\", args: [], screen: \"\"},\n  env: {}, // any extra env variables\n  cwd: \"\" // defaults to the root directory of the project\n}\n```\n\nIf the command contains sensitive information (such as a password or\nOAuth token), you can specify a `screen` command, which is what will\nbe output.\n\n###### status(type, args) Update the job status\n\nSee `strider-runner-core` for the `job.status.` events. This emits a\n`job.status.[type]` event with [jobid] + arguments.\n\n###### out(data, type) Output\n\nType defaults to `stdout`. It can be one of `stderr`, `message`,\n`error`, `warn`. `error` and `warn` are prefixed by a colored\n`[STRIDER] WARN | ERROR` and sent to `stderr`. `message` is prefixed\nby a colored `[STRIDER]` and sent to `stdout`.\n\n###### Other context data\n\nYou shouldn't need to use these, but they're there.\n\n- logger\n- io\n\n#### Extra config\n\n##### Icon\n\nJob plugins can also define an `icon` in the `strider.json` object,\nwhich is the path to a 48x48 image that will be shown on the project\nconfiguration page when a user is enabling plugins.\n\n##### Config Panel\n\nIf the plugin requires special configuration, it can also define a\n`panel` object in `strider.json`, which looks like:\n\n```javascript\n\"panel\": {\n  \"src\": \"path/to/file.html\",\n  \"controller\": \"NameOfCtrl\" // defaults to [pluginid]Controller\n}\n```\n\nDefine the angular controller in `/static/project_config.js`, which\nwill be loaded.\n\nSee [strider-webhooks](https://github.com/Strider-CD/strider-webhooks)\nfor an example of a custom config panel.\n\n### Basic\n\nThis is where you do whatever you want. It will not be listed in the\nUI anywhere automatically, so user configuration will require your own\ningenuity. If the need arises, we might expose some kind of config on\nthe system level to strider administrators, but not at the moment.\n\n#### Worker\n\nYou can listen for events, but you shouldn't run any tests or interact\nwith the source code in any way. For that, write a `job` plugin.\n\n```javascript\nmodule.exports = function (context, job, done) {\n};\n```\n\n#### Webapp\n\n```javascript\nmodule.exports = function (context, done) {\n};\n```\n\n\n### Webapp Plugin Context\n\nThis is what gets passed into the `basic` init function, as well as\nthe `listen` and `routes` functions of various plugin types.\n\n- config -- main strider config\n- emitter -- for passing events\n- models\n- logger\n- middleware\n- auth\n- app -- the express app\n- registerBlock\n\n#### `registerBlock(name, cb)`\n\n```javascript\nctx.registerBlock('HeaderBrand', function(context, cb) {\n  // context has a lot of useful stuff on it:\n\n  var email = context.currentUser.user.email;\n\n  // You can do some async processing here, but bear in mind\n  // you'll be blocking the page load.\n\n  cb(null, '\u003ch1\u003eFooStrider\u003c/h1\u003e');\n});\n```\n\n#### Templates in strider.json\n\nBecause writing a bunch of `registerBlock` calls for simple pieces of template\noverrides is a little tedious, you can also use the following shortcut in your\nstrider.json:\n\n```javascript\n{\n  \"templates\": {\n    \"HeaderBrand\": \"\u003ch1\u003eAn HTML String\u003c/h1\u003e\",\n    \"FooterTOS\": \"./path/to/TOS.html\"\n  }\n}\n```\nThese are either inline strings or paths to static HTML. There is no templating\navailable for these at present.\n\n*Note* If more than one override is specified for a block, then the first one\nwill be used. At the moment this means that extensions can squash each other.\nIf you want to simply 'append' to a block, use the `registerBlock` method\nand make sure that you prefix the html you return with:\n`ctx.content` which will contain either the default html, or the content from\nprevious extensions.\n\n[config-controllers]: https://github.com/Strider-CD/strider/tree/master/client/config/controllers\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrider-cd%2Fstrider-extension-loader","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstrider-cd%2Fstrider-extension-loader","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstrider-cd%2Fstrider-extension-loader/lists"}