{"id":23744525,"url":"https://github.com/cdaringe/isobot","last_synced_at":"2025-04-12T06:24:17.028Z","repository":{"id":270294934,"uuid":"909210800","full_name":"cdaringe/isobot","owner":"cdaringe","description":"A bot to run your typed scripts, safely!","archived":false,"fork":false,"pushed_at":"2025-04-02T20:15:57.000Z","size":216,"stargazers_count":1,"open_issues_count":11,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-02T21:24:58.892Z","etag":null,"topics":["github","probot","probot-app"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cdaringe.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-28T03:15:45.000Z","updated_at":"2025-01-12T01:13:45.000Z","dependencies_parsed_at":"2024-12-30T05:22:36.067Z","dependency_job_id":"0a7edf49-332a-48e1-8dfb-307966fc3581","html_url":"https://github.com/cdaringe/isobot","commit_stats":null,"previous_names":["cdaringe/isobot"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdaringe%2Fisobot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdaringe%2Fisobot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdaringe%2Fisobot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cdaringe%2Fisobot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cdaringe","download_url":"https://codeload.github.com/cdaringe/isobot/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248526266,"owners_count":21118850,"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":["github","probot","probot-app"],"created_at":"2024-12-31T12:24:07.371Z","updated_at":"2025-04-12T06:24:17.000Z","avatar_url":"https://github.com/cdaringe.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cstyle type=\"text/css\"\u003e\nh1 {\n  display: inline;\n}\n\u003c/style\u003e\n\n\u003ch1 style=\"display: inline-block;\"\u003e\u003cimg src=\"img/pineskew.opti.svg\" width=\"30px\"\u003e isobot\u003c/h1\u003e\n\nA probot library and application to make running repo-configurable scripts easy and powerful.\n\n## What\n\n`isobot` lets you, a repo owner, write an executable script to do work.\n\n- 🟢 Code! isobot lets you, the repo owner, run code. Testable, statically analyzable, sweet code.\n- 🔴 Configuration. Most github/Probot applications use YAML or JSON _configuration_ to drive bot activity. This is hard to get correct, and offers little in the way of 1) correctness verification, 2) flexibility!\n\nHere's an example of a `.github/isobot.ts` file that sniffs for `/merge` comment commands,\nand has the bot approve and merge the PR.\n\n```ts\nexport const pipeline: Pipeline = (\n  stream,\n  {\n    rxjs: {\n      operators: { filter, mergeMap },\n    },\n  }\n) =\u003e\n  stream.pipe(\n    filter((evt) =\u003e evt.name === \"issue_comment\"),\n    mergeMap((evt) =\u003e evt.tk.gh.withPR(evt, evt.payload.issue.number)),\n    mergeMap((evt) =\u003e evt.tk.gh.withPRComments(evt, evt.payload.issue.number)),\n    filter((evt) =\u003e\n      evt.ctx.prComments.some((it) =\u003e it.body?.startsWith(\"/merge\"))\n    ),\n    mergeMap((evt) =\u003e evt.tk.gh.approvePR(evt, evt.ctx.pr.number)),\n    mergeMap((evt) =\u003e evt.tk.gh.mergePR(evt, evt.ctx.pr.number))\n  );\n```\n\n### Usage - End user\n\n```ts\n// Basic pipeline. Receive all probot events, do nothing with them\nexport const pipeline: Pipeline = (stream) =\u003e stream;\n\n// The `event`s on the stream are decorated with rich resources\nstream.pipe(\n  filter((event) =\u003e event.name === \"issue_comment\"),\n  mergeMap((event) =\u003e {\n    event.ctx; // starts off typed as the empty object, {}\n    event.tk; // toolkit!\n    const nextEvent = event.tk.updateContext(event, { foo: 123 });\n    // nextEvent.ctx.foo === 123, typed!\n\n    /**\n     * instantiated octokit rest instance!\n     * @see {@link https://octokit.github.io/rest.js/v18/}\n     */\n    event.tk.octokit;\n\n    event.tk.gh; // github toolkit. sugar functions that extend the context using updateContext\n\n    return event.tk.gh.withPRComments(evt, evt.payload.issue.number);\n  }),\n  mergeMap((event) =\u003e {\n    event.ctx.prComments; // now populated from github API!\n\n    /**\n     * Always emit/return the event from the pipeline.\n     * State should be added via update context.\n     */\n    return event;\n  })\n);\n```\n\n### Usage - Deployment\n\n`isobot` can be plugged directly into a `probot` app, as it is _library first_. It also hosts a `bin` file, such that you can install the npm package and run it directly.\n\nPlease see [probot documentation](https://probot.github.io/) for more.\n\n### Design observations\n\n- This library is new! You are invited to add context-updating sugar functions to the `toolkit/tk`.\n- You will note the use of `rxjs`. This little implementation detail (which may be a turn off for some) is present such that\n  we can very concisely express pipelines. You can _generally_ opt out if desired, so long as `pipeline` returns an observable that produces an event.\n\nYou may observe that running repo-code is intrinsically _risky_.\n\n- RISK: Repo owners are commanding the _bot_, meaning the bot has _power_ to possibly do evil.\n\n  - MITIGATION 1: Limit your bot installation. Limit events it can receive, limit repos it can act on. If you want multiple bots with different powers/capabilities, deploy `isobot` as _different_ applications with different permissions!\n  - MITIGATION 2: `isobot` files are run in a semi-protected sandbox. User code generally cannot meddle with the host application. User code _cannot_ import libraries, including node.js primitives.\n\n- This project is _new_. It is tested, but offers little capability and needs active contribution to make it great! Please see the open issues.\n\n## Appendix\n\n### Opting-out of rxjs\n\n```ts\nexport const pipeline = (\n  stream,\n  {\n    rxjs: {\n      operators: { tap },\n    },\n  }\n) =\u003e\n  stream.pipe(\n    tap(async (event) =\u003e {\n      // Regular async/await work here\n      const response = await fetch(\"https://api.example.com\");\n      const data = await response.json();\n\n      // Regular loops\n      for (const item of data) {\n        await processItem(item);\n      }\n\n      // Regular promises\n      await Promise.all([doThing1(), doThing2(), doThing3()]);\n\n      // Regular try/catch\n      try {\n        await riskyOperation();\n      } catch (err) {\n        console.error(err);\n      }\n    })\n  );\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcdaringe%2Fisobot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcdaringe%2Fisobot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcdaringe%2Fisobot/lists"}