{"id":18395406,"url":"https://github.com/runwaylab/issue-db","last_synced_at":"2026-03-05T12:32:32.335Z","repository":{"id":260869270,"uuid":"882572121","full_name":"runwaylab/issue-db","owner":"runwaylab","description":"A Ruby Gem to use GitHub Issues as a NoSQL JSON document db","archived":false,"fork":false,"pushed_at":"2026-02-03T21:06:03.000Z","size":59786,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-19T02:03:31.872Z","etag":null,"topics":["db","gem","github","issues","json","ruby"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/issue-db","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/runwaylab.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":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-11-03T06:28:38.000Z","updated_at":"2026-01-29T05:50:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"13a11b24-763b-42ae-a52a-49743d1fb7a3","html_url":"https://github.com/runwaylab/issue-db","commit_stats":null,"previous_names":["runwaylab/issue-db"],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/runwaylab/issue-db","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runwaylab%2Fissue-db","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runwaylab%2Fissue-db/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runwaylab%2Fissue-db/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runwaylab%2Fissue-db/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/runwaylab","download_url":"https://codeload.github.com/runwaylab/issue-db/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runwaylab%2Fissue-db/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30124463,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T11:11:57.947Z","status":"ssl_error","status_checked_at":"2026-03-05T11:11:29.001Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["db","gem","github","issues","json","ruby"],"created_at":"2024-11-06T02:10:52.516Z","updated_at":"2026-03-05T12:32:32.325Z","avatar_url":"https://github.com/runwaylab.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# issue-db\n\n[![test](https://github.com/runwaylab/issue-db/actions/workflows/test.yml/badge.svg)](https://github.com/runwaylab/issue-db/actions/workflows/test.yml)\n[![lint](https://github.com/runwaylab/issue-db/actions/workflows/lint.yml/badge.svg)](https://github.com/runwaylab/issue-db/actions/workflows/lint.yml)\n[![build](https://github.com/runwaylab/issue-db/actions/workflows/build.yml/badge.svg)](https://github.com/runwaylab/issue-db/actions/workflows/build.yml)\n[![acceptance](https://github.com/runwaylab/issue-db/actions/workflows/acceptance.yml/badge.svg)](https://github.com/runwaylab/issue-db/actions/workflows/acceptance.yml)\n[![release](https://github.com/runwaylab/issue-db/actions/workflows/release.yml/badge.svg)](https://github.com/runwaylab/issue-db/actions/workflows/release.yml)\n[![coverage](./docs/assets/coverage.svg)](./docs/assets/coverage.svg)\n![slsa-level3](docs/assets/slsa-level3.svg)\n\nA Ruby Gem to use GitHub Issues as a NoSQL JSON document db.\n\n![issue-db](./docs/assets/issue-db.png)\n\n## Quick Start ⚡\n\nThe `issue-db` gem uses CRUD operations to read and write data to a GitHub repository using issues as the records. The title of the issue is used as the unique key for the record and the body of the issue is used to store the data in JSON format.\n\nHere is an extremely basic example of using the `issue-db` gem:\n\n```ruby\nrequire \"issue_db\"\n\n# The GitHub repository to use as the database\nrepo = \"runwaylab/grocery-orders\"\n\n# Create a new database instance\ndb = IssueDB.new(repo)\n\n# Write a new record to the database\ndb.create(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\" ] })\n\n# Read the newly created record from the database\nrecord = db.read(\"order_number_123\")\n\nputs record.data # =\u003e {location: \"London\", items: [\"cookies\", \"espresso\"]}\n```\n\n\u003e A more detailed example can be found below.\n\n## Installation 🚚\n\nYou may install this Gem from either [RubyGems](https://rubygems.org/gems/issue-db) or [GitHub Packages](https://github.com/runwaylab/issue-db/pkgs/rubygems/issue-db).\n\nRubyGems:\n\n```ruby\nsource \"https://rubygems.org\"\n\ngem \"issue-db\", \"X.X.X\" # Replace X.X.X with the version you want to use\n```\n\nGitHub Packages:\n\n```ruby\nsource \"https://rubygems.pkg.github.com/runwaylab\" do\n  gem \"issue-db\", \"X.X.X\" # Replace X.X.X with the version you want to use\nend\n```\n\nCommand Line Installation:\n\n```sh\ngem install issue-db --version \"X.X.X\"\n```\n\n## Usage 💻\n\nThis section goes into details on the following CRUD operations are available for the `issue-db` gem.\n\nNote: All methods return the `IssueDB::Record` of the object which was involved in the operation\n\n\u003e [!IMPORTANT]  \n\u003e The key for the record is the title of the GitHub issue. This means that the key **must be unique** within the database. If you try to do any sort of duplicate operation on a key that already exists (like creating it again), the `issue-db` gem will return the existing record without modifying it. Here is an example log message where someone calls `db.create(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\" ] })` but the key already exists: `skipping issue creation and returning existing issue - an issue already exists with the key: order_number_123`. Additionally, if there are duplicates (same issue titles), then the latest issue will be returned (ex: issue 15 will be returned instead of issue 14). Basically, if you use fully unique keys, you won't ever run into this issue so please make sure to use unique keys for your records!\n\n### `db.create(key, data, options = {})`\n\n- `key` (String) - The unique key for the record. This is the title of the GitHub issue. It must be unique within the database.\n- `data` (Hash) - The data to write to the record. This can be any valid JSON data type (String, Number, Boolean, Array, Object, or nil).\n- `options` (Hash) - A hash of options to configure the create operation.\n\nExample:\n\n```ruby\nrecord = db.create(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\" ] })\n\n# with the `body_before` and `body_after` options to add markdown text before and after the data in the GitHub issue body\n# this can be useful if you want to add additional context to the data in the issue body for humans to read\n# more on this in another section of the README below\noptions = { body_before: \"some markdown text before the data\", body_after: \"some markdown text after the data\" }\nrecord = db.create(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\" ] }, options)\n\n# with the `labels` option to add additional GitHub labels to the issue (in addition to the library-managed label)\noptions = { labels: [\"priority:high\", \"customer:premium\"] }\nrecord = db.create(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\" ] }, options)\n\n# with the `assignees` option to assign GitHub users to the issue\noptions = { assignees: [\"alice\", \"bob\"] }\nrecord = db.create(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\" ] }, options)\n\n# with multiple options combined\noptions = { \n  labels: [\"priority:high\", \"customer:premium\"],\n  assignees: [\"alice\", \"bob\"],\n  body_before: \"some markdown text before the data\", \n  body_after: \"some markdown text after the data\" \n}\nrecord = db.create(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\" ] }, options)\n```\n\nNotes:\n\n- If the key already exists in the database, the `create` method will return the existing record without modifying it.\n\n### `db.read(key, options = {})`\n\n- `key` (String) - The unique key for the record. This is the title of the GitHub issue.\n- `options` (Hash) - A hash of options to configure the read operation.\n\nExample:\n\n```ruby\nrecord = db.read(\"order_number_123\")\n\n# with the `include_closed` option to include records that have been deleted (i.e. the GitHub issue is closed)\noptions = { include_closed: true }\nrecord = db.read(\"order_number_123\", options)\n```\n\n### `db.update(key, data, options = {})`\n\n- `key` (String) - The unique key for the record. This is the title of the GitHub issue.\n- `data` (Hash) - The data to write to the record. This can be any valid JSON data type (String, Number, Boolean, Array, Object, or nil).\n- `options` (Hash) - A hash of options to configure the update operation.\n\nExample:\n\n```ruby\nrecord = db.update(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\", \"chips\" ] })\n\n# with the `body_before` and `body_after` options to add markdown text before and after the data in the GitHub issue body\n# this can be useful if you want to add additional context to the data in the issue body for humans to read\n# more on this in another section of the README below\noptions = { body_before: \"# Order 123\\n\\nData:\", body_after: \"Please do not edit the body of this issue\" }\nrecord = db.update(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\", \"chips\" ] }, options)\n\n# with the `labels` option to add additional GitHub labels to the issue (in addition to the library-managed label)\noptions = { labels: [\"status:processed\", \"priority:low\"] }\nrecord = db.update(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\", \"chips\" ] }, options)\n\n# with the `assignees` option to assign GitHub users to the issue\noptions = { assignees: [\"charlie\", \"diana\"] }\nrecord = db.update(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\", \"chips\" ] }, options)\n\n# with multiple options combined\noptions = { \n  labels: [\"status:processed\", \"priority:low\"],\n  assignees: [\"charlie\", \"diana\"],\n  body_before: \"# Order 123\\n\\nData:\", \n  body_after: \"Please do not edit the body of this issue\" \n}\nrecord = db.update(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\", \"chips\" ] }, options)\n```\n\n### `db.delete(key, options = {})`\n\n- `key` (String) - The unique key for the record. This is the title of the GitHub issue.\n- `options` (Hash) - A hash of options to configure the delete operation.\n\nExample:\n\n```ruby\nrecord = db.delete(\"order_number_123\")\n\n# with the `labels` option to add additional GitHub labels to the issue before closing it\noptions = { labels: [\"archived\", \"completed\"] }\nrecord = db.delete(\"order_number_123\", options)\n\n# with the `assignees` option to assign GitHub users to the issue before closing it\noptions = { assignees: [\"alice\"] }\nrecord = db.delete(\"order_number_123\", options)\n\n# with multiple options combined\noptions = { \n  labels: [\"archived\", \"completed\"],\n  assignees: [\"alice\"]\n}\nrecord = db.delete(\"order_number_123\", options)\n```\n\n### `db.list_keys(options = {})`\n\n- `options` (Hash) - A hash of options to configure the list operation.\n\nExample:\n\n```ruby\nkeys = db.list_keys\n\n# with the `include_closed` option to include records that have been deleted (i.e. the GitHub issue is closed)\noptions = { include_closed: true }\nkeys = db.list_keys(options)\n```\n\n### `db.list(options = {})`\n\n- `options` (Hash) - A hash of options to configure the list operation.\n\nExample:\n\n```ruby\nrecords = db.list\n\n# with the `include_closed` option to include records that have been deleted (i.e. the GitHub issue is closed)\noptions = { include_closed: true }\nrecords = db.list(options)\n```\n\n### `db.refresh!`\n\nForce a refresh of the database cache. This will make a request to the GitHub API to get the latest data from the GitHub issues in the repository.\n\nThis can be useful if you have made changes to the database outside of the gem and don't want to wait for the cache to refresh. By default, the cache refreshes every 60 seconds. Modified records (such as an `update` operation) will be refreshed automatically into the cache so that subsequent reads will return the updated data. The only time you really need to worry about refreshing the cache is if you have made changes to the database outside of the gem or if there is another service using this gem that is also making changes to the database.\n\nExample:\n\n```ruby\ndb.refresh!\n```\n\n## Options 🛠\n\nThis section will go into detail around how you can configure the `issue-db` gem to behave:\n\n### Environment Variables 🌍\n\n| Name | Description | Default Value |\n|------|-------------|---------------|\n| `LOG_LEVEL` | The log level to use for the `issue-db` gem. Can be one of `DEBUG`, `INFO`, `WARN`, `ERROR`, or `FATAL` | `INFO` |\n| `ISSUE_DB_LABEL` | The label to use for the issues that are used as records in the database. This value is required and it is what this gem uses to scan a repo for the records it is aware of. | `issue-db` |\n| `ISSUE_DB_CACHE_EXPIRY` | The number of seconds to cache the database in memory. The database is cached in memory to avoid making a request to the GitHub API for every operation. The default value is 60 seconds. | `60` |\n| `GH_APP_SLEEP` | The number of seconds to sleep between requests to the GitHub API in the event of an error | `3` |\n| `GH_APP_RETRIES` | The number of retries to make when there is an error making a request to the GitHub API | `10` |\n| `GH_APP_EXPONENTIAL_BACKOFF` | Whether to use exponential backoff for retries. When `true`, sleep time doubles with each retry. When `false`, uses fixed sleep time. | `false` |\n| `GH_APP_ALGO` | The algo to use for your GitHub App if providing a private key | `RS256` |\n| `ISSUE_DB_GITHUB_TOKEN` | The GitHub personal access token to use for authenticating with the GitHub API. You can also use a GitHub app or pass in your own authenticated Octokit.rb instance | `nil` |\n\n## Labels 🏷️\n\nThe `issue-db` gem uses GitHub issue labels for organization and management. Here's how labels work:\n\n### Library-Managed Label\n\nThe gem automatically applies a library-managed label (default: `issue-db`) to all issues it creates. This label:\n\n- **Cannot be modified or removed** by users (the gem will always ensure it's present)\n- Is used to identify which issues in the repository are managed by the `issue-db` gem\n- Can be customized by setting the `ISSUE_DB_LABEL` environment variable or passing the `label` parameter to `IssueDB.new()`\n\n### Additional Custom Labels\n\nYou can add your own custom labels to issues when creating, updating, or deleting records by using the `labels` option:\n\n```ruby\n# Add custom labels when creating a record\noptions = { labels: [\"priority:high\", \"customer:premium\", \"region:europe\"] }\nrecord = db.create(\"order_123\", { product: \"laptop\" }, options)\n\n# Add custom labels when updating a record\noptions = { labels: [\"status:processed\", \"priority:low\"] }\nrecord = db.update(\"order_123\", { product: \"laptop\", status: \"shipped\" }, options)\n\n# Add custom labels before deleting (closing) a record\noptions = { labels: [\"archived\", \"completed\", \"Q4-2024\"] }\nrecord = db.delete(\"order_123\", options)\n```\n\n**Important Notes:**\n\n- Custom labels are **added in addition** to the library-managed label, not instead of it\n- If you accidentally include the library-managed label in your custom labels array, it will be automatically filtered out to prevent duplicates\n- Custom labels follow GitHub's label naming conventions and restrictions\n- Labels help with organization, filtering, and automation workflows in GitHub\n\n### Label Preservation\n\nWhen performing update or delete operations, the gem preserves existing labels by default:\n\n```ruby\n# Create a record with custom labels\noptions = { labels: [\"priority:high\", \"customer:premium\"] }\nrecord = db.create(\"order_123\", { product: \"laptop\" }, options)\n# Result: Issue has labels [\"issue-db\", \"priority:high\", \"customer:premium\"]\n\n# Update the record WITHOUT specifying labels - existing labels are preserved\nrecord = db.update(\"order_123\", { product: \"laptop\", status: \"shipped\" })\n# Result: Issue STILL has labels [\"issue-db\", \"priority:high\", \"customer:premium\"]\n\n# Update the record WITH new labels - replaces all labels (except library-managed)\noptions = { labels: [\"status:processed\", \"priority:low\"] }\nrecord = db.update(\"order_123\", { product: \"laptop\", status: \"delivered\" }, options)\n# Result: Issue now has labels [\"issue-db\", \"status:processed\", \"priority:low\"]\n```\n\n**Key Behavior:**\n\n- **Labels specified** = Replace all labels with library-managed label + specified labels\n- **No labels specified** = Preserve existing labels exactly as they are\n\n### Example with Multiple Options\n\nYou can combine labels with other options:\n\n```ruby\noptions = {\n  labels: [\"priority:high\", \"customer:vip\"],\n  body_before: \"## Order Details\\n\\nCustomer: VIP\\n\\n\",\n  body_after: \"\\n\\n---\\n*This order requires special handling*\"\n}\nrecord = db.create(\"vip_order_456\", { items: [\"premium_service\"] }, options)\n```\n\n## Assignees 👥\n\nThe `issue-db` gem supports GitHub issue assignees for task ownership and responsibility tracking. Here's how assignees work:\n\n### Basic Assignee Usage\n\nYou can assign GitHub users to issues when creating, updating, or deleting records by using the `assignees` option:\n\n```ruby\n# Assign users when creating a record\noptions = { assignees: [\"alice\", \"bob\"] }\nrecord = db.create(\"task_123\", { type: \"code_review\" }, options)\n\n# Assign users when updating a record\noptions = { assignees: [\"charlie\", \"diana\"] }\nrecord = db.update(\"task_123\", { type: \"code_review\", status: \"in_progress\" }, options)\n\n# Assign users before deleting (closing) a record\noptions = { assignees: [\"alice\"] }\nrecord = db.delete(\"task_123\", options)\n```\n\n### Assignee Preservation\n\nJust like labels, the gem preserves existing assignees by default when no assignees are specified:\n\n```ruby\n# Create a record with assignees\noptions = { assignees: [\"alice\", \"bob\"] }\nrecord = db.create(\"task_123\", { type: \"code_review\" }, options)\n# Result: Issue is assigned to alice and bob\n\n# Update the record WITHOUT specifying assignees - existing assignees are preserved\nrecord = db.update(\"task_123\", { type: \"code_review\", status: \"in_progress\" })\n# Result: Issue is STILL assigned to alice and bob\n\n# Update the record WITH new assignees - replaces all assignees\noptions = { assignees: [\"charlie\"] }\nrecord = db.update(\"task_123\", { type: \"code_review\", status: \"completed\" }, options)\n# Result: Issue is now only assigned to charlie\n```\n\n**Key Behavior:**\n\n- **Assignees specified** = Replace all assignees with the specified assignees\n- **No assignees specified** = Preserve existing assignees exactly as they are\n- **Empty array specified** = Remove all assignees from the issue\n\n### Combining Labels and Assignees\n\nYou can use both labels and assignees together for comprehensive issue management:\n\n```ruby\noptions = {\n  labels: [\"priority:high\", \"type:bug\", \"team:backend\"],\n  assignees: [\"alice\", \"bob\"],\n  body_before: \"## Bug Report\\n\\nPriority: High\\nTeam: Backend\\n\\n\",\n  body_after: \"\\n\\n---\\n*Assigned to backend team leads*\"\n}\nrecord = db.create(\"bug_456\", { \n  error: \"Database timeout\", \n  severity: \"critical\" \n}, options)\n```\n\n**Important Notes:**\n\n- Assignees must be valid GitHub usernames with access to the repository\n- You can assign up to 10 users to a single issue (GitHub's limit)\n- Invalid or inaccessible usernames will cause the API call to fail\n- Assignees help with responsibility tracking, notifications, and project management workflows\n\n## Authentication 🔒\n\nThe `issue-db` gem uses the [`Octokit.rb`](https://github.com/octokit/octokit.rb) library under the hood for interactions with the GitHub API. You have four options for authentication when using the `issue-db` gem:\n\n\u003e Note: The order displayed below is also the order of priority that this Gem uses to authenticate.\n\n1. Pass in your own authenticated `Octokit.rb` instance to the `IssueDB.new` method\n2. Pass GitHub App authentication parameters directly to the `IssueDB.new` method\n3. Use a GitHub App by setting the `ISSUE_DB_GITHUB_APP_ID`, `ISSUE_DB_GITHUB_APP_INSTALLATION_ID`, and `ISSUE_DB_GITHUB_APP_KEY` environment variables\n4. Use a GitHub personal access token by setting the `ISSUE_DB_GITHUB_TOKEN` environment variable\n\n\u003e Using a GitHub App is the suggested method\n\nHere are examples of each of these options:\n\n### Using a GitHub Personal Access Token\n\n```ruby\n# Assuming you have a GitHub personal access token set as the ISSUE_DB_GITHUB_TOKEN env var\nrequire \"issue_db\"\n\ndb = IssueDB.new(\"\u003corg\u003e/\u003crepo\u003e\") # THAT'S IT! 🎉\n```\n\n### Using GitHub App Parameters Directly\n\nYou can now pass GitHub App authentication parameters directly to the `IssueDB.new` method. This is especially useful when you want to manage authentication credentials in your application code or when you have multiple GitHub Apps for different purposes:\n\n```ruby\nrequire \"issue_db\"\n\n# Pass GitHub App credentials directly to IssueDB.new\ndb = IssueDB.new(\n  \"\u003corg\u003e/\u003crepo\u003e\",\n  app_id: 12345,                    # Your GitHub App ID\n  installation_id: 56789,          # Your GitHub App Installation ID\n  app_key: \"-----BEGIN RSA PRIVATE KEY-----\\n...\\n-----END RSA PRIVATE KEY-----\",  # Your GitHub App private key\n  app_algo: \"RS256\"                 # Optional: defaults to RS256\n)\n```\n\n**Parameters:**\n\n- `app_id` (Integer) - Your GitHub App ID (found on the App's settings page)\n- `installation_id` (Integer) - Your GitHub App Installation ID (found in the installation URL: `https://github.com/organizations/\u003corg\u003e/settings/installations/\u003cinstallation_id\u003e`)\n- `app_key` (String) - Your GitHub App private key (can be the key content as a string or a file path ending in `.pem`)\n- `app_algo` (String, optional) - The algorithm to use for JWT signing (defaults to \"RS256\")\n\n### Using a GitHub App with Environment Variables\n\nThis is the single best way to use the `issue-db` gem because GitHub Apps have increased rate limits, fine-grained permissions, and are more secure than using a personal access token. All you have to do is provide three environment variables and the `issue-db` gem will take care of the rest:\n\n- `ISSUE_DB_GITHUB_APP_ID`\n- `ISSUE_DB_GITHUB_APP_INSTALLATION_ID`\n- `ISSUE_DB_GITHUB_APP_KEY`\n\nHere is an example of how you can use a GitHub app with the `issue-db` gem:\n\n```ruby\n# Assuming you have the following three environment variables set:\n\n# 1: ISSUE_DB_GITHUB_APP_ID\n# app ids are found on the App's settings page\n\n# 2: ISSUE_DB_GITHUB_APP_INSTALLATION_ID\n# installation ids look like this:\n# https://github.com/organizations/\u003corg\u003e/settings/installations/\u003c8_digit_id\u003e\n\n# 3. ISSUE_DB_GITHUB_APP_KEY\n# app keys are found on the App's settings page and can be downloaded\n# format: \"-----BEGIN...key\\n...END-----\\n\" (this will be one super long string and that's okay)\n# make sure this key in your env is a single line string with newlines as \"\\n\"\n\n# With all three of these environment variables set, you can proceed with ease!\ndb = IssueDB.new(\"\u003corg\u003e/\u003crepo\u003e\") # THAT'S IT! 🎉\n```\n\n### Using Your Own Authenticated `Octokit.rb` Instance\n\n```ruby\nrequire \"octokit\"\n\n# Create your own authenticated Octokit.rb instance\n# You should probably set the page_size to 100 and auto_paginate to true\noctokit = Octokit::Client.new(access_token: \"\u003cTOKEN\u003e\", page_size: 100)\noctokit.auto_paginate = true\n\ndb = IssueDB.new(\"\u003corg\u003e/\u003crepo\u003e\", octokit_client: octokit)\n```\n\n## Advanced Example 🚀\n\nHere is a more advanced example of using the `issue-db` gem that demonstrates many different features of the gem:\n\n```ruby\n# Assuming you have a GitHub personal access token set as the ISSUE_DB_GITHUB_TOKEN env var\nrequire \"issue_db\"\n\n# The GitHub repository to use as the database\nrepo = \"runwaylab/grocery-orders\"\n\n# Create a new database instance\ndb = IssueDB.new(repo)\n\n# Write a new record to the database where the title of the issue is the unique key\nnew_issue = db.create(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\" ] })\n\n# View the record data and the source data which contains the GitHub issue object\nputs new_issue.data # =\u003e {location: \"London\", items: [\"cookies\", \"espresso\"]}\nputs new_issue.source_data.state # =\u003e \"open\" (the GitHub issue is open so the record is active)\nputs new_issue.source_data.html_url # =\u003e \"https://github.com/runwaylab/grocery-orders/issues/\u003cnumber\u003e\" (the URL of the GitHub issue which is the DB record)\n\n# Update the record\nupdated_issue = db.update(\"order_number_123\", { location: \"London\", items: [ \"cookies\", \"espresso\", \"chips\" ] })\n\n# View the updated record data\nputs updated_issue.data # =\u003e {location: \"London\", items: [\"cookies\", \"espresso\", \"chips\"]}\n\n# Get the record by key\nrecord = db.read(\"order_number_123\")\n\n# View the record data\nputs record.data # =\u003e {location: \"London\", items: [\"cookies\", \"espresso\", \"chips\"]}\n\n# Delete the record\ndeleted_record = db.delete(\"order_number_123\")\nputs deleted_record.source_data.state # =\u003e \"closed\" (the GitHub issue is closed as \"completed\" so the record is inactive)\n\n# List all keys in the database including closed records\nkeys = db.list_keys({ include_closed: true })\n\nputs keys # =\u003e [\"order_number_123\"]\n\n# List all records in the database including closed records\nrecords = db.list({ include_closed: true })\n\n# Inspection of the first record in the list\nputs records.first.data # =\u003e {location: \"London\", items: [\"cookies\", \"espresso\", \"chips\"]}\nputs records.first.source_data.state # =\u003e \"closed\" (the GitHub issue is closed as \"completed\" so the record is inactive)\n\n# Force a refresh of the database cache (useful if you have made changes to the database outside of the gem and don't want to wait for the cache to refresh)\ndb.refresh!\n```\n\n## Embedding Markdown in the Issue Body 📝\n\nWith this library, you can write markdown text into the issue body **in addition** to the JSON data. Pretty cool right?\n\nThis can be especially useful if you want to add additional context to the data in the issue body for humans to read. For example, you want to open an issue to track the status of an employee who has a laptop that is running an out of date operating system. You might want to do a `db.write()` operation with machine readable data but _also_ include a note for the employee to read. Here is an example of how you can do that:\n\n```ruby\nbody_before_text = \u003c\u003c~BODY\n# Out of Date Operating System 🚨\n\n\u003c!--- HUMANS: DO NOT EDIT THIS ISSUE BODY ;) --\u003e\n\nIt looks like your laptop is running an out of date operating system. This is a security risk.\n\nPlease update your OS as soon as possible.\n\n## Details\nBODY\n\nbody_after_text = \u003c\u003c~BODY\n\u003e Thank you for your attention to this matter.\nBODY\n\noptions = { body_before: body_before_text, body_after: body_after_text }\ndata = { operating_system: \"macOS 1.2.3\", last_updated_at: \"2024-09-30\", user: \"Celeste\", location: \"California\", out_of_date: true, employee_id: 123 }\nrecord = db.create(\"Out of Date OS - EmployeeID: 123\", data, options) # this assumes that employee IDs are unique in this example\n```\n\nRunning that code snippet will result in a database record (GitHub issue) being created that has the following markdown body:\n\n````markdown\n# Out of Date Operating System 🚨\n\n\u003c!--- HUMANS: DO NOT EDIT THIS ISSUE BODY ;) --\u003e\n\nIt looks like your laptop is running an out of date operating system. This is a security risk.\n\nPlease update your OS as soon as possible.\n\n## Details\n\n\u003c!--- issue-db-start --\u003e\n```json\n{\n  \"operating_system\": \"macOS 1.2.3\",\n  \"last_updated_at\": \"2024-09-30\",\n  \"user\": \"Celeste\",\n  \"location\": \"California\",\n  \"out_of_date\": true\n}\n```\n\u003c!--- issue-db-end --\u003e\n\n\u003e Thank you for your attention to this matter.\n````\n\nHere is a screenshot of exactly how this issue would render in GitHub:\n\n![example1](./docs/assets/example1.png)\n\nAnd here is a link to the actual issue in GitHub: [issue link](https://github.com/runwaylab/issue-db/issues/19)\n\nNow the best part about this, is that we can still use the `db.read()` method flawlessly and get the data back in a machine readable format, and we can even get back the markdown text as well! Here is an example of how you can do that:\n\n```ruby\nrecord = db.read(\"Out of Date OS - EmployeeID: 123\")\n\nputs record.data # =\u003e {\"operating_system\"=\u003e\"macOS 1.2.3\", \"last_updated_at\"=\u003e\"2024-09-30\", \"user\"=\u003e\"Celeste\", \"location\"=\u003e\"California\", \"out_of_date\"=\u003etrue, \"employee_id\"=\u003e123}\nputs record.body_before # the markdown text before the data in the issue body (as seen above, not going to repeat it here - its long)\nputs record.body_after # ditto ^\n# puts record.source_data.body # useful for getting the raw markdown text of the issue body as is\n```\n\nHere is a screenshot of the output of the script above:\n\n![example2](./docs/assets/example2.png)\n\n## Record Attributes 📦\n\nDatabase \"items\" are called Records in this library. Records are represented by a `IssueDB::Record` object. Records are actually just GitHub issues under the hood!\n\nRecords have the following attributes:\n\n- `key` (String) - The unique key for the record. This is the title of the GitHub issue.\n- `data` (Hash) - The data for the record. This can be any valid JSON data type (String, Number, Boolean, Array, Object, or nil).\n- `source_data` (Octokit::Issue) - The GitHub issue object that is the source of the record. This value is returned by Octokit and is a Sawyer::Resource object.\n- `body_before` (String) - The markdown text before the data in the issue body.\n- `body_after` (String) - The markdown text after the data in the issue body.\n\nExample:\n\n```ruby\nrecord = db.read(\"order_number_123\")\n\nputs record.key # =\u003e \"order_number_123\"\nputs record.data # =\u003e { \"location\"=\u003e\"London\", \"items\"=\u003e[ \"cookies\", \"espresso\", \"chips\" ] }\nputs record.data[\"location\"] # =\u003e \"London\"\nputs record.source_data.state # =\u003e \"open\" (the GitHub issue is open so the record is active)\nputs record.body_before # =\u003e \"some markdown text before the data\"\nputs record.body_after # =\u003e \"some markdown text after the data\"\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frunwaylab%2Fissue-db","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frunwaylab%2Fissue-db","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frunwaylab%2Fissue-db/lists"}