github-to-sqlite 2.8.3__tar.gz → 2.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,14 +1,19 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: github-to-sqlite
3
- Version: 2.8.3
3
+ Version: 2.9.1
4
4
  Summary: Save data from GitHub to a SQLite database
5
- Home-page: https://github.com/dogsheep/github-to-sqlite
6
5
  Author: Simon Willison
7
- License: Apache License, Version 2.0
8
- Platform: UNKNOWN
9
- Description-Content-Type: text/markdown
10
- Provides-Extra: test
6
+ License-Expression: Apache-2.0
11
7
  License-File: LICENSE
8
+ Requires-Dist: sqlite-utils>4
9
+ Requires-Dist: requests
10
+ Requires-Dist: pyyaml
11
+ Requires-Python: >=3.10
12
+ Project-URL: Homepage, https://github.com/dogsheep/github-to-sqlite
13
+ Project-URL: Changelog, https://github.com/dogsheep/github-to-sqlite/releases
14
+ Project-URL: Issues, https://github.com/dogsheep/github-to-sqlite/issues
15
+ Project-URL: CI, https://github.com/dogsheep/github-to-sqlite/actions
16
+ Description-Content-Type: text/markdown
12
17
 
13
18
  # github-to-sqlite
14
19
 
@@ -94,13 +99,25 @@ You can use the `--pull-request` option one or more times to load specific pull
94
99
 
95
100
  Note that the `merged_by` column on the `pull_requests` table will only be populated for pull requests that are loaded using the `--pull-request` option - the GitHub API does not return this field for pull requests that are loaded in bulk.
96
101
 
102
+ You can load only pull requests in a certain state with the `--state` option:
103
+
104
+ $ github-to-sqlite pull-requests --state=open github.db simonw/datasette
105
+
106
+ Pull requests across an entire organization (or more than one) can be loaded with `--org`:
107
+
108
+ $ github-to-sqlite pull-requests --state=open --org=psf --org=python github.db
109
+
110
+ You can use a search query to find pull requests. Note that no more than 1000 will be loaded (this is a GitHub API limitation), and some data will be missing (base and head SHAs). When using searches, other filters are ignored; put all criteria into the search itself:
111
+
112
+ $ github-to-sqlite pull-requests --search='org:python defaultdict state:closed created:<2023-09-01' github.db
113
+
97
114
  Example: [pull_requests table](https://github-to-sqlite.dogsheep.net/github/pull_requests)
98
115
 
99
116
  ## Fetching issue comments for a repository
100
117
 
101
118
  The `issue-comments` command retrieves all of the comments on all of the issues in a repository.
102
119
 
103
- It is recommended you run `issues` first, so that each imported comment can have a foreign key poining to its issue.
120
+ It is recommended you run `issues` first, so that each imported comment can have a foreign key pointing to its issue.
104
121
 
105
122
  $ github-to-sqlite issues github.db simonw/datasette
106
123
  $ github-to-sqlite issue-comments github.db simonw/datasette
@@ -113,7 +130,7 @@ Example: [issue_comments table](https://github-to-sqlite.dogsheep.net/github/iss
113
130
 
114
131
  ## Fetching commits for a repository
115
132
 
116
- The `commits` command retrieves details of all of the commits for one or more repositories. It currently fetches the sha, commit message and author and committer details - it does no retrieve the full commit body.
133
+ The `commits` command retrieves details of all of the commits for one or more repositories. It currently fetches the SHA, commit message and author and committer details; it does not retrieve the full commit body.
117
134
 
118
135
  $ github-to-sqlite commits github.db simonw/datasette simonw/sqlite-utils
119
136
 
@@ -168,7 +185,7 @@ You can pass more than one username to fetch for multiple users or organizations
168
185
 
169
186
  $ github-to-sqlite repos github.db simonw dogsheep
170
187
 
171
- Add the `--readme` option to save the README for the repo in a column called `readme`. Add `--readme-html` to save the HTML rendered version of the README into a collumn called `readme_html`.
188
+ Add the `--readme` option to save the README for the repo in a column called `readme`. Add `--readme-html` to save the HTML rendered version of the README into a column called `readme_html`.
172
189
 
173
190
  Example: [repos table](https://github-to-sqlite.dogsheep.net/github/repos)
174
191
 
@@ -228,7 +245,7 @@ You can fetch a list of every emoji supported by GitHub using the `emojis` comma
228
245
 
229
246
  $ github-to-sqlite emojis github.db
230
247
 
231
- This will create a table callad `emojis` with a primary key `name` and a `url` column.
248
+ This will create a table called `emojis` with a primary key `name` and a `url` column.
232
249
 
233
250
  If you add the `--fetch` option the command will also fetch the binary content of the images and place them in an `image` column:
234
251
 
@@ -247,7 +264,7 @@ The `github-to-sqlite get` command provides a convenient shortcut for making aut
247
264
 
248
265
  This will make an authenticated call to the URL you provide and pretty-print the resulting JSON to the console.
249
266
 
250
- You can ommit the `https://api.github.com/` prefix, for example:
267
+ You can omit the `https://api.github.com/` prefix, for example:
251
268
 
252
269
  $ github-to-sqlite get /gists
253
270
 
@@ -258,5 +275,3 @@ Many GitHub APIs are [paginated using the HTTP Link header](https://docs.github.
258
275
  You can outline newline-delimited JSON for each item using `--nl`. This can be useful for streaming items into another tool.
259
276
 
260
277
  $ github-to-sqlite get /users/simonw/repos --nl
261
-
262
-
@@ -1,15 +1,3 @@
1
- Metadata-Version: 2.1
2
- Name: github-to-sqlite
3
- Version: 2.8.3
4
- Summary: Save data from GitHub to a SQLite database
5
- Home-page: https://github.com/dogsheep/github-to-sqlite
6
- Author: Simon Willison
7
- License: Apache License, Version 2.0
8
- Platform: UNKNOWN
9
- Description-Content-Type: text/markdown
10
- Provides-Extra: test
11
- License-File: LICENSE
12
-
13
1
  # github-to-sqlite
14
2
 
15
3
  [![PyPI](https://img.shields.io/pypi/v/github-to-sqlite.svg)](https://pypi.org/project/github-to-sqlite/)
@@ -94,13 +82,25 @@ You can use the `--pull-request` option one or more times to load specific pull
94
82
 
95
83
  Note that the `merged_by` column on the `pull_requests` table will only be populated for pull requests that are loaded using the `--pull-request` option - the GitHub API does not return this field for pull requests that are loaded in bulk.
96
84
 
85
+ You can load only pull requests in a certain state with the `--state` option:
86
+
87
+ $ github-to-sqlite pull-requests --state=open github.db simonw/datasette
88
+
89
+ Pull requests across an entire organization (or more than one) can be loaded with `--org`:
90
+
91
+ $ github-to-sqlite pull-requests --state=open --org=psf --org=python github.db
92
+
93
+ You can use a search query to find pull requests. Note that no more than 1000 will be loaded (this is a GitHub API limitation), and some data will be missing (base and head SHAs). When using searches, other filters are ignored; put all criteria into the search itself:
94
+
95
+ $ github-to-sqlite pull-requests --search='org:python defaultdict state:closed created:<2023-09-01' github.db
96
+
97
97
  Example: [pull_requests table](https://github-to-sqlite.dogsheep.net/github/pull_requests)
98
98
 
99
99
  ## Fetching issue comments for a repository
100
100
 
101
101
  The `issue-comments` command retrieves all of the comments on all of the issues in a repository.
102
102
 
103
- It is recommended you run `issues` first, so that each imported comment can have a foreign key poining to its issue.
103
+ It is recommended you run `issues` first, so that each imported comment can have a foreign key pointing to its issue.
104
104
 
105
105
  $ github-to-sqlite issues github.db simonw/datasette
106
106
  $ github-to-sqlite issue-comments github.db simonw/datasette
@@ -113,7 +113,7 @@ Example: [issue_comments table](https://github-to-sqlite.dogsheep.net/github/iss
113
113
 
114
114
  ## Fetching commits for a repository
115
115
 
116
- The `commits` command retrieves details of all of the commits for one or more repositories. It currently fetches the sha, commit message and author and committer details - it does no retrieve the full commit body.
116
+ The `commits` command retrieves details of all of the commits for one or more repositories. It currently fetches the SHA, commit message and author and committer details; it does not retrieve the full commit body.
117
117
 
118
118
  $ github-to-sqlite commits github.db simonw/datasette simonw/sqlite-utils
119
119
 
@@ -168,7 +168,7 @@ You can pass more than one username to fetch for multiple users or organizations
168
168
 
169
169
  $ github-to-sqlite repos github.db simonw dogsheep
170
170
 
171
- Add the `--readme` option to save the README for the repo in a column called `readme`. Add `--readme-html` to save the HTML rendered version of the README into a collumn called `readme_html`.
171
+ Add the `--readme` option to save the README for the repo in a column called `readme`. Add `--readme-html` to save the HTML rendered version of the README into a column called `readme_html`.
172
172
 
173
173
  Example: [repos table](https://github-to-sqlite.dogsheep.net/github/repos)
174
174
 
@@ -228,7 +228,7 @@ You can fetch a list of every emoji supported by GitHub using the `emojis` comma
228
228
 
229
229
  $ github-to-sqlite emojis github.db
230
230
 
231
- This will create a table callad `emojis` with a primary key `name` and a `url` column.
231
+ This will create a table called `emojis` with a primary key `name` and a `url` column.
232
232
 
233
233
  If you add the `--fetch` option the command will also fetch the binary content of the images and place them in an `image` column:
234
234
 
@@ -247,7 +247,7 @@ The `github-to-sqlite get` command provides a convenient shortcut for making aut
247
247
 
248
248
  This will make an authenticated call to the URL you provide and pretty-print the resulting JSON to the console.
249
249
 
250
- You can ommit the `https://api.github.com/` prefix, for example:
250
+ You can omit the `https://api.github.com/` prefix, for example:
251
251
 
252
252
  $ github-to-sqlite get /gists
253
253
 
@@ -258,5 +258,3 @@ Many GitHub APIs are [paginated using the HTTP Link header](https://docs.github.
258
258
  You can outline newline-delimited JSON for each item using `--nl`. This can be useful for streaming items into another tool.
259
259
 
260
260
  $ github-to-sqlite get /users/simonw/repos --nl
261
-
262
-
@@ -1,5 +1,6 @@
1
1
  import click
2
2
  import datetime
3
+ import itertools
3
4
  import pathlib
4
5
  import textwrap
5
6
  import os
@@ -104,19 +105,54 @@ def issues(db_path, repo, issue_ids, auth, load):
104
105
  type=click.Path(file_okay=True, dir_okay=False, allow_dash=True, exists=True),
105
106
  help="Load pull-requests JSON from this file instead of the API",
106
107
  )
107
- def pull_requests(db_path, repo, pull_request_ids, auth, load):
108
+ @click.option(
109
+ "--org",
110
+ "orgs",
111
+ help="Fetch all pull requests from this GitHub organization",
112
+ multiple=True,
113
+ )
114
+ @click.option(
115
+ "--state",
116
+ help="Only fetch pull requests in this state",
117
+ )
118
+ @click.option(
119
+ "--search",
120
+ help="Find pull requests with a search query",
121
+ )
122
+ def pull_requests(db_path, repo, pull_request_ids, auth, load, orgs, state, search):
108
123
  "Save pull_requests for a specified repository, e.g. simonw/datasette"
109
124
  db = sqlite_utils.Database(db_path)
110
125
  token = load_token(auth)
111
- repo_full = utils.fetch_repo(repo, token)
112
- utils.save_repo(db, repo_full)
113
126
  if load:
127
+ repo_full = utils.fetch_repo(repo, token)
128
+ utils.save_repo(db, repo_full)
114
129
  pull_requests = json.load(open(load))
130
+ utils.save_pull_requests(db, pull_requests, repo_full)
131
+ elif search:
132
+ repos_seen = set()
133
+ search += " is:pr"
134
+ pull_requests = utils.fetch_searched_pulls_or_issues(search, token)
135
+ for pull_request in pull_requests:
136
+ pr_repo_url = pull_request["repository_url"]
137
+ if pr_repo_url not in repos_seen:
138
+ pr_repo = utils.fetch_repo(url=pr_repo_url)
139
+ utils.save_repo(db, pr_repo)
140
+ repos_seen.add(pr_repo_url)
141
+ utils.save_pull_requests(db, [pull_request], pr_repo)
115
142
  else:
116
- pull_requests = utils.fetch_pull_requests(repo, token, pull_request_ids)
117
-
118
- pull_requests = list(pull_requests)
119
- utils.save_pull_requests(db, pull_requests, repo_full)
143
+ if orgs:
144
+ repos = itertools.chain.from_iterable(
145
+ utils.fetch_all_repos(token=token, org=org) for org in orgs
146
+ )
147
+ else:
148
+ repos = [utils.fetch_repo(repo, token)]
149
+ for repo_full in repos:
150
+ utils.save_repo(db, repo_full)
151
+ repo = repo_full["full_name"]
152
+ pull_requests = utils.fetch_pull_requests(
153
+ repo, state, token, pull_request_ids
154
+ )
155
+ utils.save_pull_requests(db, pull_requests, repo_full)
120
156
  utils.ensure_db_shape(db)
121
157
 
122
158
 
@@ -464,7 +500,9 @@ def scrape_dependents(db_path, repos, auth, verbose):
464
500
  {
465
501
  "repo": repo_full["id"],
466
502
  "dependent": dependent_id,
467
- "first_seen_utc": datetime.datetime.utcnow().isoformat(),
503
+ "first_seen_utc": datetime.datetime.now(datetime.timezone.utc)
504
+ .replace(tzinfo=None)
505
+ .isoformat(),
468
506
  },
469
507
  pk=("repo", "dependent"),
470
508
  foreign_keys=(
@@ -2,6 +2,7 @@ import base64
2
2
  import requests
3
3
  import re
4
4
  import time
5
+ import urllib.parse
5
6
  import yaml
6
7
 
7
8
  FTS_CONFIG = {
@@ -74,16 +75,17 @@ FOREIGN_KEYS = [
74
75
 
75
76
 
76
77
  class GitHubError(Exception):
77
- def __init__(self, message, status_code):
78
+ def __init__(self, message, status_code, headers=None):
78
79
  self.message = message
79
80
  self.status_code = status_code
81
+ self.headers = headers
80
82
 
81
83
  @classmethod
82
84
  def from_response(cls, response):
83
85
  message = response.json()["message"]
84
86
  if "git repository is empty" in message.lower():
85
87
  cls = GitHubRepositoryEmpty
86
- return cls(message, response.status_code)
88
+ return cls(message, response.status_code, response.headers)
87
89
 
88
90
 
89
91
  class GitHubRepositoryEmpty(GitHubError):
@@ -169,8 +171,11 @@ def save_pull_requests(db, pull_requests, repo):
169
171
  # Add repo key
170
172
  pull_request["repo"] = repo["id"]
171
173
  # Pull request _links can be flattened to just their URL
172
- pull_request["url"] = pull_request["_links"]["html"]["href"]
173
- pull_request.pop("_links")
174
+ if "_links" in pull_request:
175
+ pull_request["url"] = pull_request["_links"]["html"]["href"]
176
+ pull_request.pop("_links")
177
+ else:
178
+ pull_request["url"] = pull_request["pull_request"]["html_url"]
174
179
  # Extract user
175
180
  pull_request["user"] = save_user(db, pull_request["user"])
176
181
  labels = pull_request.pop("labels")
@@ -178,8 +183,9 @@ def save_pull_requests(db, pull_requests, repo):
178
183
  if pull_request.get("merged_by"):
179
184
  pull_request["merged_by"] = save_user(db, pull_request["merged_by"])
180
185
  # Head sha
181
- pull_request["head"] = pull_request["head"]["sha"]
182
- pull_request["base"] = pull_request["base"]["sha"]
186
+ if "head" in pull_request:
187
+ pull_request["head"] = pull_request["head"]["sha"]
188
+ pull_request["base"] = pull_request["base"]["sha"]
183
189
  # Extract milestone
184
190
  if pull_request["milestone"]:
185
191
  pull_request["milestone"] = save_milestone(
@@ -223,6 +229,11 @@ def save_pull_requests(db, pull_requests, repo):
223
229
 
224
230
 
225
231
  def save_user(db, user):
232
+ # Under some conditions, GitHub caches removed repositories with
233
+ # stars and ends up leaving dangling `None` user references.
234
+ if user is None:
235
+ return None
236
+
226
237
  # Remove all url fields except avatar_url and html_url
227
238
  to_save = {
228
239
  key: value
@@ -286,12 +297,13 @@ def save_issue_comment(db, comment):
286
297
  return last_pk
287
298
 
288
299
 
289
- def fetch_repo(full_name, token=None):
300
+ def fetch_repo(full_name=None, token=None, url=None):
290
301
  headers = make_headers(token)
291
302
  # Get topics:
292
303
  headers["Accept"] = "application/vnd.github.mercy-preview+json"
293
- owner, slug = full_name.split("/")
294
- url = "https://api.github.com/repos/{}/{}".format(owner, slug)
304
+ if url is None:
305
+ owner, slug = full_name.split("/")
306
+ url = "https://api.github.com/repos/{}/{}".format(owner, slug)
295
307
  response = requests.get(url, headers=headers)
296
308
  response.raise_for_status()
297
309
  return response.json()
@@ -352,7 +364,7 @@ def fetch_issues(repo, token=None, issue_ids=None):
352
364
  yield from issues
353
365
 
354
366
 
355
- def fetch_pull_requests(repo, token=None, pull_request_ids=None):
367
+ def fetch_pull_requests(repo, state=None, token=None, pull_request_ids=None):
356
368
  headers = make_headers(token)
357
369
  headers["accept"] = "application/vnd.github.v3+json"
358
370
  if pull_request_ids:
@@ -364,11 +376,20 @@ def fetch_pull_requests(repo, token=None, pull_request_ids=None):
364
376
  response.raise_for_status()
365
377
  yield response.json()
366
378
  else:
367
- url = "https://api.github.com/repos/{}/pulls?state=all&filter=all".format(repo)
379
+ state = state or "all"
380
+ url = f"https://api.github.com/repos/{repo}/pulls?state={state}"
368
381
  for pull_requests in paginate(url, headers):
369
382
  yield from pull_requests
370
383
 
371
384
 
385
+ def fetch_searched_pulls_or_issues(query, token=None):
386
+ headers = make_headers(token)
387
+ url = "https://api.github.com/search/issues?"
388
+ url += urllib.parse.urlencode({"q": query})
389
+ for pulls_or_issues in paginate(url, headers):
390
+ yield from pulls_or_issues["items"]
391
+
392
+
372
393
  def fetch_issue_comments(repo, token=None, issue=None):
373
394
  assert "/" in repo
374
395
  headers = make_headers(token)
@@ -439,13 +460,17 @@ def fetch_stargazers(repo, token=None):
439
460
  yield from stargazers
440
461
 
441
462
 
442
- def fetch_all_repos(username=None, token=None):
443
- assert username or token, "Must provide username= or token= or both"
463
+ def fetch_all_repos(username=None, token=None, org=None):
464
+ assert (
465
+ username or token or org
466
+ ), "Must provide username= or token= or org= or a combination"
444
467
  headers = make_headers(token)
445
468
  # Get topics for each repo:
446
469
  headers["Accept"] = "application/vnd.github.mercy-preview+json"
447
470
  if username:
448
471
  url = "https://api.github.com/users/{}/repos".format(username)
472
+ elif org:
473
+ url = "https://api.github.com/orgs/{}/repos".format(org)
449
474
  else:
450
475
  url = "https://api.github.com/user/repos"
451
476
  for repos in paginate(url, headers):
@@ -463,6 +488,7 @@ def fetch_user(username=None, token=None):
463
488
 
464
489
 
465
490
  def paginate(url, headers=None):
491
+ url += ("&" if "?" in url else "?") + "per_page=100"
466
492
  while url:
467
493
  response = requests.get(url, headers=headers)
468
494
  # For HTTP 204 no-content this yields an empty list
@@ -671,7 +697,10 @@ def ensure_foreign_keys(db):
671
697
  for expected_foreign_key in FOREIGN_KEYS:
672
698
  table, column, table2, column2 = expected_foreign_key
673
699
  if (
674
- expected_foreign_key not in db[table].foreign_keys
700
+ expected_foreign_key not in {
701
+ (fk.table, fk.column, fk.other_table, fk.other_column)
702
+ for fk in db[table].foreign_keys
703
+ }
675
704
  and
676
705
  # Ensure all tables and columns exist
677
706
  db[table].exists()
@@ -726,7 +755,7 @@ def scrape_dependents(repo, verbose=False):
726
755
  yield from repos
727
756
  # next page?
728
757
  try:
729
- next_link = soup.select(".paginate-container")[0].find("a", text="Next")
758
+ next_link = soup.select(".paginate-container")[0].find("a", string="Next")
730
759
  except IndexError:
731
760
  break
732
761
  if next_link is not None:
@@ -0,0 +1,39 @@
1
+ [project]
2
+ name = "github-to-sqlite"
3
+ version = "2.9.1"
4
+ description = "Save data from GitHub to a SQLite database"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ license = "Apache-2.0"
8
+ license-files = ["LICENSE"]
9
+ dependencies = [
10
+ "sqlite-utils>4",
11
+ "requests",
12
+ "PyYAML",
13
+ ]
14
+
15
+ [[project.authors]]
16
+ name = "Simon Willison"
17
+
18
+ [project.urls]
19
+ Homepage = "https://github.com/dogsheep/github-to-sqlite"
20
+ Changelog = "https://github.com/dogsheep/github-to-sqlite/releases"
21
+ Issues = "https://github.com/dogsheep/github-to-sqlite/issues"
22
+ CI = "https://github.com/dogsheep/github-to-sqlite/actions"
23
+
24
+ [project.scripts]
25
+ github-to-sqlite = "github_to_sqlite.cli:cli"
26
+
27
+ [dependency-groups]
28
+ dev = [
29
+ "pytest",
30
+ "requests-mock",
31
+ "bs4",
32
+ ]
33
+
34
+ [build-system]
35
+ requires = ["uv_build>=0.9.18,<0.12.0"]
36
+ build-backend = "uv_build"
37
+
38
+ [tool.uv.build-backend]
39
+ module-root = ""
@@ -0,0 +1,37 @@
1
+ [project]
2
+ name = "github-to-sqlite"
3
+ version = "2.9.1"
4
+ description = "Save data from GitHub to a SQLite database"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ authors = [{name = "Simon Willison"}]
8
+ license = "Apache-2.0"
9
+ license-files = ["LICENSE"]
10
+ dependencies = [
11
+ "sqlite-utils>4",
12
+ "requests",
13
+ "PyYAML",
14
+ ]
15
+
16
+ [project.urls]
17
+ Homepage = "https://github.com/dogsheep/github-to-sqlite"
18
+ Changelog = "https://github.com/dogsheep/github-to-sqlite/releases"
19
+ Issues = "https://github.com/dogsheep/github-to-sqlite/issues"
20
+ CI = "https://github.com/dogsheep/github-to-sqlite/actions"
21
+
22
+ [project.scripts]
23
+ github-to-sqlite = "github_to_sqlite.cli:cli"
24
+
25
+ [dependency-groups]
26
+ dev = [
27
+ "pytest",
28
+ "requests-mock",
29
+ "bs4",
30
+ ]
31
+
32
+ [build-system]
33
+ requires = ["uv_build>=0.9.18,<0.12.0"]
34
+ build-backend = "uv_build"
35
+
36
+ [tool.uv.build-backend]
37
+ module-root = ""
@@ -1,248 +0,0 @@
1
- # github-to-sqlite
2
-
3
- [![PyPI](https://img.shields.io/pypi/v/github-to-sqlite.svg)](https://pypi.org/project/github-to-sqlite/)
4
- [![Changelog](https://img.shields.io/github/v/release/dogsheep/github-to-sqlite?include_prereleases&label=changelog)](https://github.com/dogsheep/github-to-sqlite/releases)
5
- [![Tests](https://github.com/dogsheep/github-to-sqlite/workflows/Test/badge.svg)](https://github.com/dogsheep/github-to-sqlite/actions?query=workflow%3ATest)
6
- [![License](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/dogsheep/github-to-sqlite/blob/main/LICENSE)
7
-
8
- Save data from GitHub to a SQLite database.
9
-
10
- <!-- toc -->
11
-
12
- - [Demo](#demo)
13
- - [How to install](#how-to-install)
14
- - [Authentication](#authentication)
15
- - [Fetching issues for a repository](#fetching-issues-for-a-repository)
16
- - [Fetching pull requests for a repository](#fetching-pull-requests-for-a-repository)
17
- - [Fetching issue comments for a repository](#fetching-issue-comments-for-a-repository)
18
- - [Fetching commits for a repository](#fetching-commits-for-a-repository)
19
- - [Fetching releases for a repository](#fetching-releases-for-a-repository)
20
- - [Fetching tags for a repository](#fetching-tags-for-a-repository)
21
- - [Fetching contributors to a repository](#fetching-contributors-to-a-repository)
22
- - [Fetching repos belonging to a user or organization](#fetching-repos-belonging-to-a-user-or-organization)
23
- - [Fetching specific repositories](#fetching-specific-repositories)
24
- - [Fetching repos that have been starred by a user](#fetching-repos-that-have-been-starred-by-a-user)
25
- - [Fetching users that have starred specific repos](#fetching-users-that-have-starred-specific-repos)
26
- - [Fetching GitHub Actions workflows](#fetching-github-actions-workflows)
27
- - [Scraping dependents for a repository](#scraping-dependents-for-a-repository)
28
- - [Fetching emojis](#fetching-emojis)
29
- - [Making authenticated API calls](#making-authenticated-api-calls)
30
-
31
- <!-- tocstop -->
32
-
33
- ## Demo
34
-
35
- https://github-to-sqlite.dogsheep.net/ hosts a [Datasette](https://datasette.io/) demo of a database created by [running this tool](https://github.com/dogsheep/github-to-sqlite/blob/main/.github/workflows/deploy-demo.yml#L40-L60) against all of the repositories in the [Dogsheep GitHub organization](https://github.com/dogsheep), plus the [datasette](https://github.com/simonw/datasette) and [sqlite-utils](https://github.com/simonw/sqlite-utils) repositories.
36
-
37
- ## How to install
38
-
39
- $ pip install github-to-sqlite
40
-
41
- ## Authentication
42
-
43
- Create a GitHub personal access token: https://github.com/settings/tokens
44
-
45
- Run this command and paste in your new token:
46
-
47
- $ github-to-sqlite auth
48
-
49
- This will create a file called `auth.json` in your current directory containing the required value. To save the file at a different path or filename, use the `--auth=myauth.json` option.
50
-
51
- As an alternative to using an `auth.json` file you can add your access token to an environment variable called `GITHUB_TOKEN`.
52
-
53
- ## Fetching issues for a repository
54
-
55
- The `issues` command retrieves all of the issues belonging to a specified repository.
56
-
57
- $ github-to-sqlite issues github.db simonw/datasette
58
-
59
- If an `auth.json` file is present it will use the token from that file. It works without authentication for public repositories but you should be aware that GitHub have strict IP-based rate limits for unauthenticated requests.
60
-
61
- You can point to a different location of `auth.json` using `-a`:
62
-
63
- $ github-to-sqlite issues github.db simonw/datasette -a /path/to/auth.json
64
-
65
- You can use the `--issue` option one or more times to load specific issues:
66
-
67
- $ github-to-sqlite issues github.db simonw/datasette --issue=1
68
-
69
- Example: [issues table](https://github-to-sqlite.dogsheep.net/github/issues)
70
-
71
- ## Fetching pull requests for a repository
72
-
73
- While pull requests are a type of issue, you will get more information on pull requests by pulling them separately. For example, whether a pull request has been merged and when.
74
-
75
- Following the API of issues, the `pull-requests` command retrieves all of the pull requests belonging to a specified repository.
76
-
77
- $ github-to-sqlite pull-requests github.db simonw/datasette
78
-
79
- You can use the `--pull-request` option one or more times to load specific pull request:
80
-
81
- $ github-to-sqlite pull-requests github.db simonw/datasette --pull-request=81
82
-
83
- Note that the `merged_by` column on the `pull_requests` table will only be populated for pull requests that are loaded using the `--pull-request` option - the GitHub API does not return this field for pull requests that are loaded in bulk.
84
-
85
- Example: [pull_requests table](https://github-to-sqlite.dogsheep.net/github/pull_requests)
86
-
87
- ## Fetching issue comments for a repository
88
-
89
- The `issue-comments` command retrieves all of the comments on all of the issues in a repository.
90
-
91
- It is recommended you run `issues` first, so that each imported comment can have a foreign key poining to its issue.
92
-
93
- $ github-to-sqlite issues github.db simonw/datasette
94
- $ github-to-sqlite issue-comments github.db simonw/datasette
95
-
96
- You can use the `--issue` option to only load comments for a specific issue within that repository, for example:
97
-
98
- $ github-to-sqlite issue-comments github.db simonw/datasette --issue=1
99
-
100
- Example: [issue_comments table](https://github-to-sqlite.dogsheep.net/github/issue_comments)
101
-
102
- ## Fetching commits for a repository
103
-
104
- The `commits` command retrieves details of all of the commits for one or more repositories. It currently fetches the sha, commit message and author and committer details - it does no retrieve the full commit body.
105
-
106
- $ github-to-sqlite commits github.db simonw/datasette simonw/sqlite-utils
107
-
108
- The command accepts one or more repositories.
109
-
110
- By default it will stop as soon as it sees a commit that has previously been retrieved. You can force it to retrieve all commits (including those that have been previously inserted) using `--all`.
111
-
112
- Example: [commits table](https://github-to-sqlite.dogsheep.net/github/commits)
113
-
114
- ## Fetching releases for a repository
115
-
116
- The `releases` command retrieves the releases for one or more repositories.
117
-
118
- $ github-to-sqlite releases github.db simonw/datasette simonw/sqlite-utils
119
-
120
- The command accepts one or more repositories.
121
-
122
- Example: [releases table](https://github-to-sqlite.dogsheep.net/github/releases)
123
-
124
- ## Fetching tags for a repository
125
-
126
- The `tags` command retrieves all of the tags for one or more repositories.
127
-
128
- $ github-to-sqlite tags github.db simonw/datasette simonw/sqlite-utils
129
-
130
- Example: [tags table](https://github-to-sqlite.dogsheep.net/github/tags)
131
-
132
- ## Fetching contributors to a repository
133
-
134
- The `contributors` command retrieves details of all of the contributors for one or more repositories.
135
-
136
- $ github-to-sqlite contributors github.db simonw/datasette simonw/sqlite-utils
137
-
138
- The command accepts one or more repositories. It populates a `contributors` table, with foreign keys to `repos` and `users` and a `contributions` table listing the number of commits to that repository for each contributor.
139
-
140
- Example: [contributors table](https://github-to-sqlite.dogsheep.net/github/contributors)
141
-
142
- ## Fetching repos belonging to a user or organization
143
-
144
- The `repos` command fetches repos belonging to a user or organization.
145
-
146
- Without any other arguments, this command will fetch all repos that the currently authenticated user owns, collaborates on or can access via one of their organizations:
147
-
148
- $ github-to-sqlite repos github.db
149
-
150
- To fetch repos belonging to a specific user or organization, provide their username as an argument:
151
-
152
- $ github-to-sqlite repos github.db dogsheep # organization
153
- $ github-to-sqlite repos github.db simonw # user
154
-
155
- You can pass more than one username to fetch for multiple users or organizations at once:
156
-
157
- $ github-to-sqlite repos github.db simonw dogsheep
158
-
159
- Add the `--readme` option to save the README for the repo in a column called `readme`. Add `--readme-html` to save the HTML rendered version of the README into a collumn called `readme_html`.
160
-
161
- Example: [repos table](https://github-to-sqlite.dogsheep.net/github/repos)
162
-
163
- ## Fetching specific repositories
164
-
165
- You can use `-r` with the `repos` command one or more times to fetch just specific repositories.
166
-
167
- $ github-to-sqlite repos github.db -r simonw/datasette -r dogsheep/github-to-sqlite
168
-
169
- ## Fetching repos that have been starred by a user
170
-
171
- The `starred` command fetches the repos that have been starred by a user.
172
-
173
- $ github-to-sqlite starred github.db simonw
174
-
175
- If you are using an `auth.json` file you can omit the username to retrieve the starred repos for the authenticated user.
176
-
177
- Example: [stars table](https://github-to-sqlite.dogsheep.net/github/stars)
178
-
179
- ## Fetching users that have starred specific repos
180
-
181
- The `stargazers` command fetches the users that have starred the specified repos.
182
-
183
- $ github-to-sqlite stargazers github.db simonw/datasette dogsheep/github-to-sqlite
184
-
185
- You can specify one or more repository using `owner/repo` syntax.
186
-
187
- Users fetched using this command will be inserted into the `users` table. Many-to-many records showing which repository they starred will be added to the `stars` table.
188
-
189
- ## Fetching GitHub Actions workflows
190
-
191
- The `workflows` command fetches the YAML workflow configurations from each repository's `.github/workflows` directory and parses them to populate `workflows`, `jobs` and `steps` tables.
192
-
193
- $ github-to-sqlite workflows github.db simonw/datasette dogsheep/github-to-sqlite
194
-
195
- You can specify one or more repository using `owner/repo` syntax.
196
-
197
- Example: [workflows table](https://github-to-sqlite.dogsheep.net/github/workflows), [jobs table](https://github-to-sqlite.dogsheep.net/github/jobs), [steps table](https://github-to-sqlite.dogsheep.net/github/steps)
198
-
199
- ## Scraping dependents for a repository
200
-
201
- The GitHub dependency graph can show other GitHub projects that depend on a specific repo, for example [simonw/datasette/network/dependents](https://github.com/simonw/datasette/network/dependents).
202
-
203
- This data is not yet available through the GitHub API. The `scrape-dependents` command scrapes those pages and uses the GitHub API to load full versions of the dependent repositories.
204
-
205
- $ github-to-sqlite scrape-dependents github.db simonw/datasette
206
-
207
- The command accepts one or more repositories.
208
-
209
- Add `-v` for verbose output.
210
-
211
- Example: [dependents table](https://github-to-sqlite.dogsheep.net/github/dependents?_sort_desc=first_seen_utc)
212
-
213
- ## Fetching emojis
214
-
215
- You can fetch a list of every emoji supported by GitHub using the `emojis` command:
216
-
217
- $ github-to-sqlite emojis github.db
218
-
219
- This will create a table callad `emojis` with a primary key `name` and a `url` column.
220
-
221
- If you add the `--fetch` option the command will also fetch the binary content of the images and place them in an `image` column:
222
-
223
- $ github-to-sqlite emojis emojis.db -f
224
- [########----------------------------] 397/1799 22% 00:03:43
225
-
226
- You can then use the [datasette-render-images](https://github.com/simonw/datasette-render-images) plugin to browse them visually.
227
-
228
- Example: [emojis table](https://github-to-sqlite.dogsheep.net/github/emojis)
229
-
230
- ## Making authenticated API calls
231
-
232
- The `github-to-sqlite get` command provides a convenient shortcut for making authenticated calls to the API. Once you have created your `auth.json` file (or set a `GITHUB_TOKEN` environment variable) you can use it like this:
233
-
234
- $ github-to-sqlite get https://api.github.com/gists
235
-
236
- This will make an authenticated call to the URL you provide and pretty-print the resulting JSON to the console.
237
-
238
- You can ommit the `https://api.github.com/` prefix, for example:
239
-
240
- $ github-to-sqlite get /gists
241
-
242
- Many GitHub APIs are [paginated using the HTTP Link header](https://docs.github.com/en/rest/guides/traversing-with-pagination). You can follow this pagination and output a list of all of the resulting items using `--paginate`:
243
-
244
- $ github-to-sqlite get /users/simonw/repos --paginate
245
-
246
- You can outline newline-delimited JSON for each item using `--nl`. This can be useful for streaming items into another tool.
247
-
248
- $ github-to-sqlite get /users/simonw/repos --nl
@@ -1,12 +0,0 @@
1
- LICENSE
2
- README.md
3
- setup.py
4
- github_to_sqlite/__init__.py
5
- github_to_sqlite/cli.py
6
- github_to_sqlite/utils.py
7
- github_to_sqlite.egg-info/PKG-INFO
8
- github_to_sqlite.egg-info/SOURCES.txt
9
- github_to_sqlite.egg-info/dependency_links.txt
10
- github_to_sqlite.egg-info/entry_points.txt
11
- github_to_sqlite.egg-info/requires.txt
12
- github_to_sqlite.egg-info/top_level.txt
@@ -1,4 +0,0 @@
1
-
2
- [console_scripts]
3
- github-to-sqlite=github_to_sqlite.cli:cli
4
-
@@ -1,8 +0,0 @@
1
- sqlite-utils>=2.7.2
2
- requests
3
- PyYAML
4
-
5
- [test]
6
- pytest
7
- requests-mock
8
- bs4
@@ -1 +0,0 @@
1
- github_to_sqlite
@@ -1,4 +0,0 @@
1
- [egg_info]
2
- tag_build =
3
- tag_date = 0
4
-
@@ -1,32 +0,0 @@
1
- from setuptools import setup
2
- import os
3
-
4
- VERSION = "2.8.3"
5
-
6
-
7
- def get_long_description():
8
- with open(
9
- os.path.join(os.path.dirname(os.path.abspath(__file__)), "README.md"),
10
- encoding="utf8",
11
- ) as fp:
12
- return fp.read()
13
-
14
-
15
- setup(
16
- name="github-to-sqlite",
17
- description="Save data from GitHub to a SQLite database",
18
- long_description=get_long_description(),
19
- long_description_content_type="text/markdown",
20
- author="Simon Willison",
21
- url="https://github.com/dogsheep/github-to-sqlite",
22
- license="Apache License, Version 2.0",
23
- version=VERSION,
24
- packages=["github_to_sqlite"],
25
- entry_points="""
26
- [console_scripts]
27
- github-to-sqlite=github_to_sqlite.cli:cli
28
- """,
29
- install_requires=["sqlite-utils>=2.7.2", "requests", "PyYAML"],
30
- extras_require={"test": ["pytest", "requests-mock", "bs4"]},
31
- tests_require=["github-to-sqlite[test]"],
32
- )