joeyespo/grip
GitHub: joeyespo/grip
一个使用 Python 和 Flask 的本地服务器,用于在提交前预览和核对 GitHub README.md 文件的渲染效果。
Stars: 6783 | Forks: 438
# Grip -- GitHub Readme 即时预览
[][pypi]
[](https://saythanks.io/to/joeyespo)
在提交到 GitHub 之前渲染本地 Readme 文件。
Grip 是一个使用 Python 编写的命令行服务器应用程序,它使用
[GitHub markdown API][markdown] 来渲染本地 Readme 文件。样式
和渲染直接来自 GitHub,因此您将确切知道它将如何显示。
对 Readme 的更改将在不
需要页面刷新的情况下即时反映在浏览器中。
## 动机
有时您只想在提交和推送到 GitHub 之前看到确切的 Readme
结果。
尤其是在进行 [Readme 驱动开发][rdd] 时。
## 安装
要安装 grip,只需:
```
$ pip install grip
```
在 OS X 上,您也可以使用 Homebrew 安装:
```
$ brew install grip
```
## 用法
要渲染存储库的 Readme:
```
$ cd myrepo
$ grip
* Running on http://localhost:6419/
```
现在打开浏览器并访问 [http://localhost:6419](http://localhost:6419/)。
或者使用 `-b` 运行,Grip 将为您打开一个新的浏览器标签页。
您也可以指定端口:
```
$ grip 80
* Running on http://localhost:80/
```
或者指定一个明确的文件:
```
$ grip AUTHORS.md
* Running on http://localhost:6419/
```
或者,您可以直接运行 `grip` 并访问 [localhost:6419/AUTHORS.md][AUTHORS.md]
因为 grip 支持相对 URL。
您可以组合前面的示例。或者指定主机名而不是端口。或者同时提供两者。
```
$ grip AUTHORS.md 80
* Running on http://localhost:80/
```
```
$ grip CHANGES.md 0.0.0.0
* Running on http://0.0.0.0:6419/
```
```
$ grip . 0.0.0.0:80
* Running on http://0.0.0.0:80/
```
您甚至可以绕过服务器并 **导出** 到单个 HTML 文件,其中所有样式和资源都内联:
```
$ grip --export
Exporting to README.html
```
通过第二个参数控制输出名称:
```
$ grip README.md --export index.html
Exporting to index.html
```
如果您正在导出多个文件,可以使用 `--no-inline` 防止样式内联以节省空间:
```
$ grip README.md --export --no-inline introduction.html
Exporting to introduction.html
```
也支持从 **stdin** 和 **stdout** 读写,允许您将 Grip 与其他程序结合使用:
```
$ cat README.md | grip -
* Running on http://localhost:6419/
```
```
$ grip AUTHORS.md --export - | bcat
```
```
$ cat README.md | grip --export - | less
```
这使您可以通过在终端中直接输入 Markdown 来快速测试效果:
```
$ grip -
Hello **world**!
^D
* Running on http://localhost:6419/
```
*注意:`^D` 表示 `Ctrl+D`,它在 Linux 和 OS X 上有效。在 Windows 上,您需要使用 `Ctrl+Z`。*
也支持渲染用户内容,例如 **评论** 和 **问题**,并带有可选的存储库上下文以链接到问题:
```
$ grip --user-content --context=joeyespo/grip
* Running on http://localhost:6419/
```
有关更多详细信息和选项,请参阅帮助:
```
$ grip -h
```
## 访问
Grip 力求尽可能接近 GitHub。为了实现这一点,grip
使用 [GitHub 的 Markdown API][markdown],因此对其渲染引擎的更改会立即反映出来,而无需升级 grip。
然而,由于这一点,您可能会遇到 API 的每小时速率限制。如果发生这种情况,grip 提供了一种使用您的凭据访问 API 的方式,以解锁更高的速率限制。
```
$ grip --user --pass
```
或者使用 [个人访问令牌][] 并设置空范围(请注意,如果您的 GitHub 账户已设置双重身份验证,则*需要*令牌):
```
$ grip --pass
```
您可以将这些选项[保存在本地配置文件中](#configuration)。
出于安全考虑,强烈建议您**使用访问令牌而不是密码**。(您也可以通过配置 Grip 从 [密码管理器][keychain-access] 获取密码来保持密码安全。)
还有一个[进行中的分支][offline-renderer]提供
**离线渲染**。一旦这更接近 GitHub 的表现,它将在 CLI 中公开,并最终作为无法访问 API 时的无缝后备引擎使用。
Grip 始终通过 HTTPS 访问 GitHub,
因此您的 README 和凭据受到保护。
## 提示
以下是社区其他人使用 Grip 的方式。
想分享您自己的吗?[向 joeyespo 致意][twitter] 或 [提交拉取请求](#contributing)。
#### 创建 GitHub Wiki 的本地镜像
```
$ git clone https://github.com/YOUR_USERNAME/YOUR_REPOSITORY.wiki.git
$ cd YOUR_REPOSITORY.wiki
$ grip
```
*由 [Joshua Gourneau](https://twitter.com/gourneau/status/636329126643658753) 提供。*
#### 从一组链接的 Readme 文件生成 HTML 文档
1. 进入目录:
$ cd YOUR_DIR
$ export GRIPURL=$(pwd)
2. 包含所有资源,通过设置 `CACHE_DIRECTORY` [配置变量](#configuration):
$ echo "CACHE_DIRECTORY = '$(pwd)/assets'" >> ~/.grip/settings.py
3. 使用 Grip 导出所有 Markdown 文件,并将绝对资源路径替换为相对路径:
$ for f in *.md; do grip --export $f --no-inline; done
$ for f in *.html; do sed -i '' "s?$GRIPURL/??g" $f; done
您可以使用以下命令将 HTML 文件集压缩为 `docs.tgz`:
```
$ tar -czvf docs.tgz `ls | grep [\.]html$` assets
```
寻找跨平台解决方案?这里有一个等效的 [Python 脚本](https://gist.github.com/mrexmelle/659abc02ae1295d60647)。
*由 [Matthew R. Tanudjaja](https://github.com/mrexmelle) 提供。*
## 配置
要自定义 Grip,请创建 `~/.grip/settings.py`,然后添加以下一个或多个变量:
- `HOST`:未作为 CLI 参数提供时使用的主机,默认为 `localhost`
- `PORT`:未作为 CLI 参数提供时使用的端口,默认为 `6419`
- `DEBUG`:是否在发生错误时使用 Flask 的调试器,默认为 `False`
- `DEBUG_GRIP`:发生错误时打印扩展信息,默认为 `False`
- `API_URL`:GitHub API 的基本 URL,例如 GitHub Enterprise 实例的 URL。默认为 `https://api.github.com`
- `CACHE_DIRECTORY`:相对于 `~/.grip` 的缓存资源目录(此值会通过以下过滤器运行:`CACHE_DIRECTORY.format(version=__version__)`),默认为 `'cache-{version}'`
- `AUTOREFRESH`:文件更改时是否自动刷新 Readme 内容,默认为 `True`
- `QUIET`:不打印扩展信息,默认为 `False`
- `STYLE_URLS`:将添加到渲染页面的其他 URL,默认为 `[]`
- `USERNAME`:未作为 CLI 参数提供时使用的用户名,默认为 `None`
- `PASSWORD`:未作为 CLI 参数提供时使用的密码或 [个人访问令牌][](*请不要在此处保存密码*)。相反,请使用访问令牌或在此代码中从 [密码管理器][keychain-access] 获取密码,默认为 `None`
请注意这是一个 Python 文件。如果您看到 `'X' is not defined` 错误,
您可能遗漏了某些引号。例如:
```
USERNAME = 'your-username'
PASSWORD = 'your-personal-access-token'
```
#### 环境变量
- `GRIPHOME`:指定替代的 `settings.py` 位置,默认为 `~/.grip`
- `GRIPURL`:Grip 服务器的 URL,默认为 `/__/grip`
#### 高级用法
此文件是一个正常的 Python 脚本,因此您可以添加更高级的配置。
例如,要从环境变量读取设置并在未设置时提供默认值:
```
PORT = os.environ.get('GRIP_PORT', 8080)
```
## API
您可以直接使用 Python 访问 API,并在您自己的项目中使用它:
```
from grip import serve
serve(port=8080)
* Running on http://localhost:8080/
```
直接运行主程序:
```
from grip import main
main(argv=['-b', '8080'])
* Running on http://localhost:8080/
```
或访问底层的 Flask 应用程序以获得更大的灵活性:
```
from grip import create_app
grip_app = create_app(user_content=True)
# 使用于您自己的应用
```
### 文档
#### 服务
运行本地服务器并在浏览器中访问位于 `path` 的 Readme 文件。
```
serve(path=None, host=None, port=None, user_content=False, context=None, username=None, password=None, render_offline=False, render_wide=False, render_inline=False, api_url=None, title=None, autorefresh=True, browser=False, grip_class=None)
```
- `path`:要渲染的文件名,或包含 Readme 文件的目录,默认为当前工作目录
- `host`:监听的主机,默认为 HOST 配置变量
- `port`:监听的端口,默认为 PORT 配置变量
- `user_content`:是否将文档渲染为 [用户内容][],例如用户评论或问题
- `context`:当 `user_content` 为 true 时使用的项目上下文,
格式为 `username/project`
- `username`:用于向 GitHub 验证以扩展 API 限制的用户名
- `password`:用于向 GitHub 验证以扩展 API 限制的密码
- `render_offline`:是否使用 [Python-Markdown][] 本地渲染注意:这是一项正在进行中的工作)
- `render_wide`:是否渲染宽页面,默认为 `False`(当与 `user_content` 一起使用时无效)
- `render_inline`:是否将样式内联到 HTML 文件中
- `api_url`:GitHub API 的不同基本 URL,例如 GitHub Enterprise 实例。默认为公共 API https://api.github.com。
- `title`:页面标题,默认为从 `path` 推导
- `autorefresh`:当 Readme 文件更改时自动更新渲染内容,默认为 `True`
- `browser`:服务器启动后打开浏览器标签页,默认为 `False`
- `grip_class`:使用自定义 [Grip 类](#class-gripflask)
#### 导出
将指定的 Readme 文件写入 HTML 文件,其中包含内联样式和资源。
```
export(path=None, user_content=False, context=None, username=None, password=None, render_offline=False, render_wide=False, render_inline=True, out_filename=None, api_url=None, title=None, quiet=None, theme='light', grip_class=None)
```
- `path`:要渲染的文件名,或包含 Readme 文件的目录,默认为当前工作目录
- `user_content`:是否将文档渲染为 [用户内容][],例如用户评论或问题
- `context`:当 `user_content` 为 true 时使用的项目上下文,
格式为 `username/project`
- `username`:用于向 GitHub 验证以扩展 API 限制的用户名
- `password`:用于向 GitHub 验证以扩展 API 限制的密码
- `render_offline`:是否使用 [Python-Markdown][] 本地渲染(注意:这是一项正在进行中的工作)
- `render_wide`:是否渲染宽页面,默认为 `False`(当与 `user_content` 一起使用时无效)
- `render_inline`:是否将样式内联到 HTML 文件中(注意:与其它 API 函数不同,默认为 `True`)
- `out_filename`:要写入的文件名,默认为 `.html`
- `api_url`:GitHub API 的不同基本 URL,例如 GitHub Enterprise 实例。默认为公共 API https://api.github.com。
- `title`:页面标题,默认为从 `path` 推导
- `quiet`:不打印到终端
- `theme`:查看 Markdown 文件的主题(浅色或深色模式)。有效选项为 ("light", "dark")。默认为 "light"。
- `grip_class`:使用自定义 [Grip 类](#class-gripflask)
#### 创建应用
创建一个 Flask 应用程序,用于渲染和提供 Readme 文件。
这与 `serve` 和 `export` 使用相同的应用程序,并在可用时初始化缓存,
使用缓存的样式。
```
create_app(path=None, user_content=False, context=None, username=None, password=None, render_offline=False, render_wide=False, render_inline=False, api_url=None, title=None, text=None, grip_class=None)
```
- `path`:要渲染的文件名,或包含 Readme 文件的目录,默认为当前工作目录
- `user_content`:是否将文档渲染为 [用户内容][],例如用户评论或问题
- `context`:当 `user_content` 为 true 时使用的项目上下文,
格式为 `username/project`
- `username`:用于向 GitHub 验证以扩展 API 限制的用户名
- `password`:用于向 GitHub 验证以扩展 API 限制的密码
- `render_offline`:是否使用 [Python-Markdown][] 本地渲染(注意:这是一项正在进行中的工作)
- `render_wide`:是否渲染宽页面,默认为 `False`(当与 `user_content` 一起使用时无效)
- `render_inline`:是否将样式内联到 HTML 文件中
- `api_url`:GitHub API 的不同基本 URL,例如 GitHub Enterprise 实例。默认为公共 API https://api.github.com。
- `title`:页面标题,默认为从 `path` 推导
- `text`:要渲染的 Markdown 字符串或流,而不是从 `path` 加载(注意:`path` 可用于设置页面标题)
- `grip_class`:使用自定义 [Grip 类](#class-gripflask)
#### 渲染应用
渲染由 `create_app` 创建的应用程序并返回 HTML,
该 HTML 通常会出现在访问该路由时。
```
render_app(app, route='/')
```
- `app`:要渲染的 Flask 应用程序
- `route`:要渲染的路由,默认为 `'/'`
#### 渲染内容
在不使用缓存的情况下渲染指定的 Markdown 文本。
```
render_content(text, user_content=False, context=None, username=None, password=None, render_offline=False, api_url=None, title=None)
```
- `text`:要渲染的 Markdown 文本
- `user_content`:是否将文档渲染为 [用户内容][],例如用户评论或问题
- `context`:当 `user_content` 为 true 时使用的项目上下文,
格式为 `username/project`
- `username`:用于向 GitHub 验证以扩展 API 限制的用户名
- `password`:用于向 GitHub 验证以扩展 API 限制的密码
- `render_offline`:是否使用 [Python-Markdown][] 本地渲染(注意:这是一项正在进行中的工作)
- `api_url`:GitHub API 的不同基本 URL,当不使用离线渲染器时这是必需的。
- `title`:页面标题,默认为从 `path` 推导
#### 渲染页面
渲染来自指定路径或文本的 Markdown,不使用缓存,
并返回一个 HTML 页面,该页面类似于 GitHub Readme 视图。
```
render_page(path=None, user_content=False, context=None, username=None, password=None, render_offline=False, render_wide=False, render_inline=False, api_url=None, title=None, text=None, quiet=None, theme='light', grip_class=None)
```
- `path`:用于页面标题的路径,如果为 None 则渲染 `'README.md'`
- `user_content`:是否将文档渲染为 [用户内容][],例如用户评论或问题
- `context`:当 `user_content` 为 true 时使用的项目上下文,
格式为 `username/project`
- `username`:用于向 GitHub 验证以扩展 API 限制的用户名
- `password`:用于向 GitHub 验证以扩展 API 限制的密码
- `render_offline`:是否使用 [Python-Markdown][] 离线渲染(注意:这是一项正在进行中的工作)
- `render_wide`:是否渲染宽页面,默认为 `False`(当与 `user_content` 一起使用时无效)
- `render_inline`:是否将样式内联到 HTML 文件中
- `api_url`:GitHub API 的不同基本 URL,例如 GitHub Enterprise 实例。默认为公共 API https://api.github.com。
- `title`:页面标题,默认为从 `path` 推导
- `text`:要渲染的 Markdown 字符串或流,而不是从 `path` 加载(注意:`path` 可用于设置页面标题)
- `quiet`:不打印到终端
- `theme`:查看 Markdown 文件的主题(浅色或深色模式)。有效选项为 ("light", "dark")。默认为 "light"。
- `grip_class`:使用自定义 [Grip 类](#class-gripflask)
#### 清除缓存
清除缓存的样式和资源。
```
clear_cache(grip_class=None)
```
#### 主函数
使用指定的参数运行 Grip。
```
main(argv=None, force_utf8=True)
```
- `argv`:要运行的参数,默认为 `sys.argv[1:]`
- `force_utf8`:将当前 Python 实例的默认编码设置为 `utf-8`。这对 Python 3 没有影响,因为 Unicode 默认已处理。
### 类
#### 类 Grip(Flask)
一个可以服务包含 README 的文件或目录的 Flask 应用程序。
```
Grip(source=None, auth=None, renderer=None, assets=None, render_wide=None, render_inline=None, title=None, autorefresh=None, quiet=None, theme='light', grip_url=None, static_url_path=None, instance_path=None, **kwargs)
```
##### 默认渲染器
返回使用当前配置的默认渲染器。仅在构造函数中未设置 renderer 时使用。
```
Grip.default_renderer()
```
##### 默认资源管理器
返回使用当前配置的默认资源管理器。仅在构造函数中未设置 asset_manager 时使用。
```
Grip.default_asset_manager()
```
##### 添加内容类型
如果缺少 `application/x-font-woff` 和 `application/octet-stream` 内容类型,则添加它们。在初始化时覆盖以添加其他内容类型。
```
Grip.add_content_types()
```
##### 清除缓存
清除已下载的资源。
```
Grip.clear_cache()
```
##### 渲染
渲染应用程序并返回将通常在浏览器中显示的 HTML Unicode。
```
Grip.render(route=None)
```
- `route`:要渲染的路由,默认为 `/`
##### 运行
启动服务器以渲染 Readme。此方法内部调用 [Flask.run][]。
```
Grip.run(host=None, port=None, debug=None, use_reloader=None, open_browser=False)
```
- `host`:要监听的主机名。将其设置为 `'0.0.0.0'` 可使服务器对外部也可访问,默认为 `'localhost'`
- `port`:Web 服务器的端口,默认为 `6419`
- `debug`:如果提供,则启用或禁用调试模式。请参见 [Flask.debug][]。
- `use_reloader`:如果模块已更改,是否自动重新启动 Python 进程。默认为 `False`,除非指定 `DEBUG_GRIP` 设置。
- `open_browser`:服务器启动时打开浏览器到该地址
#### 类 AlreadyRunningError(RuntimeError)
当 `Grip.run` 在服务器已运行时调用时引发。
```
AlreadyRunningError()
```
#### 类 ReadmeNotFoundError(NotFoundError 或 IOError)
当指定的 Readme 找不到时引发。
```
ReadmeNotFoundError(path=None, message=None)
```
#### 类 ReadmeAssetManager(object)
管理用于 Readme 页面的样式和字体资源。这是一个抽象基类。
```
ReadmeAssetManager(cache_path, style_urls=None)
```
#### 类 GitHubAssetManager(ReadmeAssetManager)
管理用于 Readme 页面的样式和字体资源。将 cache_path 设置为 None 以禁用缓存。
#### 类 ReadmeReader(object)
从 URL 子路径读取 Readme 内容。这是一个抽象基类。
```
ReadmeReader()
```
#### 类 DirectoryReader(ReadmeReader)
从 URL 子路径读取 Readme 文件。
```
DirectoryReader(path=None, silent=False)
```
#### 类 TextReader(ReadmeReader)
从提供的 Unicode 字符串读取 Readme 内容。
```
TextReader(text, display_filename=None)
```
#### 类 StdinReader(TextReader)
从标准输入读取 Readme 文本。
```
StdinReader(display_filename=None)
```
#### 类 ReadmeRenderer(object)
渲染 Readme。这是一个抽象基类。
```
ReadmeRenderer(user_content=None, context=None)
```
#### 类 GitHubRenderer(ReadmeRenderer)
使用 GitHub Markdown API 渲染指定的 Readme。
```
GitHubRenderer(user_content=None, context=None, api_url=None, raw=None)
```
#### 类 OfflineRenderer(ReadmeRenderer)
使用纯 Python 本地渲染指定的 Readme。注意:这目前是一项未完成的功能。
```
OfflineRenderer(user_content=None, context=None)
```
### 常量
#### SUPPORTED_TITLES
GitHub 上常见的 Markdown 文件标题。
```
SUPPORTED_TITLES = ['README', 'Home']
```
- `filename`:要读取的 UTF-8 文件。
#### SUPPORTED_EXTENSIONS
由 [GitHub][markdown] 定义的受支持扩展名。
```
SUPPORTED_EXTENSIONS = ['.md', '.markdown']
```
#### DEFAULT_FILENAMES
此常量包含 Grip 查找的文件名(未提供文件时)。
```
DEFAULT_FILENAMES = [title + ext
for title in SUPPORTED_TITLES
for ext in SUPPORTED_EXTENSIONS]
```
#### DEFAULT_FILENAME
此常量包含默认的 Readme 文件名,即:
```
DEFAULT_FILENAME = DEFAULT_FILENAMES[0] # README.md
```
#### DEFAULT_GRIPHOME
此常量指向如果未指定 `GRIPHOME`
[环境变量](#environment-variables) 的默认值。
```
DEFAULT_GRIPHOME = '~/.grip'
```
#### DEFAULT_GRIPURL
Grip 服务器及其所有资源的默认 URL:
```
DEFAULT_GRIPURL = '/__/grip'
```
#### DEFAULT_API_URL
默认的 app_url 值:
```
DEFAULT_API_URL = 'https://api.github.com'
```
## 测试
安装软件包和测试依赖项:
```
$ pip install -e .[tests]
```
使用 [pytest][] 运行测试:
```
$ pytest
```
或者在您进行修改时重新运行测试,使用 [pytest-watch][]:
```
$ ptw
```
#### 外部假设测试
如果遇到 Grip 问题,很可能是因为对 GitHub API 的假设已被破坏。要验证这一点,请运行:
```
$ pytest -m assumption
```
由于外部假设依赖互联网连接,您可能希望在本地开发时跳过它们。使用 `-x` 在第一次失败时停止测试:
```
$ pytest -xm "not assumption"
```
或者使用 [pytest-watch][]:
```
$ ptw -- -xm "not assumption"
```
## 贡献
1. 查看开放问题或打开新问题以开始讨论您的功能想法或发现的错误
2. 叉出存储库并进行更改
3. 打开新的拉取请求
如果您使用此软件经常?
:smiley:
标签:GitHub API, Homebrew, HTTP 服务, Markdown 渲染, PyPI, Python, RDD, Readme-driven development, Readme 渲染, SEO 预览, 前端渲染, 即时预览, 开发辅助, 文档工具, 无后门, 本地服务器, 本地预览, 网页预览, 逆向工具, 静态站点