Grip -- GitHub Readme 即时预览
在发送到 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。
或者使用 -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
还支持从标准输入读取和写入到标准输出,允许你将 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 <your-username> --pass <your-password>
或者使用一个空作用域的[个人访问令牌][personal access token](注意,如果你的 GitHub 账户设置了两步验证,则必须使用令牌):
$ grip --pass <token>
你可以在本地配置中保存这些选项。出于安全考虑,强烈建议你使用访问令牌而不是密码。(你也可以通过配置 Grip 来[从密码管理器获取密码][keychain-access]来保证密码安全。)
还有一个[正在开发的分支][offline-renderer]提供离线渲染。一旦这个功能更精确地模仿 GitHub,它就会在 CLI 中公开,并最终作为一个无缝的后备引擎,在无法访问 API 时使用。
Grip 始终通过 HTTPS 访问 GitHub,因此你的 README 和凭据是受保护的。
技巧
以下是社区成员使用 Grip 的方式。
想分享你自己的技巧吗?[在 Twitter 上向 @joeyespo 问好][twitter]或提交一个拉取请求。
创建 Github Wiki 的本地镜像
$ git clone https://github.com/YOUR_USERNAME/YOUR_REPOSITORY.wiki.git
$ cd YOUR_REPOSITORY.wiki
$ grip
由 Joshua Gourneau 提供。
从一组链接的 README 文件生成 HTML 文档
-
进入目录:
$ cd YOUR_DIR $ export GRIPURL=$(pwd)
-
通过设置
CACHE_DIRECTORY
配置变量来包含所有资源:$ echo "CACHE_DIRECTORY = '$(pwd)/assets'" >> ~/.grip/settings.py
-
使用 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 脚本。
由 Matthew R. Tanudjaja 提供。
配置
要自定义 Grip,创建 ~/.grip/settings.py
,然后添加以下一个或多个变量:
HOST
:当未通过命令行参数提供时使用的主机,默认为localhost
PORT
:当未通过命令行参数提供时使用的端口,默认为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
:当未通过命令行参数提供时使用的用户名,默认为None
PASSWORD
:当未通过命令行参数提供时使用的密码或[个人访问令牌][](请不要在此处保存密码。 请使用访问令牌或使用此代码[从密码管理器获取密码][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/
直接运行main:
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)
# 在你自己的应用中使用
文档
serve
运行本地服务器并在浏览器中访问时渲染位于 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
:是否将文档渲染为[用户内容][user-content],如用户评论或问题context
:当user_content
为真时使用的项目上下文,格式为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实例的URL。默认为公共API https://api.github.comtitle
:页面标题,默认从path
派生autorefresh
:Readme文件更改时是否自动更新渲染内容,默认为True
browser
:服务器启动后是否在浏览器中打开标签页,默认为False
grip_class
:使用自定义Grip类
export
将指定的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
:是否将文档渲染为[用户内容][user-content],如用户评论或问题context
:当user_content
为真时使用的项目上下文,格式为username/project
username
:用于向GitHub认证以扩展API限制的用户password
:用于向GitHub认证以扩展API限制的密码render_offline
:是否使用[Python-Markdown][]在本地渲染(注意:这是一项进行中的工作)render_wide
:是否渲染宽页面,默认为False
(与user_content
一起使用时无效)render_inline
:是否将样式内联到HTML文件中(注意:与其他API函数不同,这里默认为True
)out_filename
:要写入的文件名,默认为<in_filename>.html
api_url
:GitHub API的不同基础URL,例如GitHub Enterprise实例的URL。默认为公共API https://api.github.comtitle
:页面标题,默认从path
派生quiet
:是否不向终端打印输出theme
:查看markdown文件的主题(浅色模式或深色模式)。有效选项("light","dark")。默认:"light"grip_class
:使用自定义Grip类
create_app
创建一个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实例的URL。默认为公共API https://api.github.com。title
: 页面标题,默认从path
派生text
: 要渲染的Markdown文本字符串或流,而不是从path
加载(注意:path
可用于设置页面标题)grip_class
: 使用自定义Grip类
render_app
渲染由create_app
创建的应用程序,并返回通常访问该路由时出现的HTML。
render_app(app, route='/')
app
: 要渲染的Flask应用程序route
: 要渲染的路由,默认为'/'
render_content
渲染指定的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,例如GitHub Enterprise实例的URL。不使用离线渲染器时需要此参数。title
: 页面标题,默认从path
派生
render_page
从指定路径或文本渲染markdown,不进行缓存,并返回类似GitHub README视图的HTML页面。
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实例的URL。默认为公共API https://api.github.com。title
: 页面标题,默认从path
派生text
: 要渲染的Markdown文本字符串或流,而不是从path
加载(注意:path
可用于设置页面标题)quiet
: 不在终端打印theme
: 查看markdown文件的主题(浅色模式或深色模式)。有效选项("light", "dark")。默认:"light"。grip_class
: 使用自定义Grip类
clear_cache
清除缓存的样式和资源。
clear_cache(grip_class=None)
main
使用指定参数运行Grip。
main(argv=None, force_utf8=True)
argv
: 要运行的参数,默认为sys.argv[1:]
force_utf8
: 在当前Python实例中将默认编码设置为utf-8
。这对Python 3没有影响,因为默认情况下已处理Unicode
类
class 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)
default_renderer
使用当前配置返回默认渲染器。仅当构造函数中renderer设置为None时使用。
Grip.default_renderer()
default_asset_manager
使用当前配置返回默认资源管理器。仅当构造函数中asset_manager设置为None时使用。
Grip.default_asset_manager()
add_content_types
如果缺少application/x-font-woff和application/octet-stream内容类型,则添加它们。重写此方法以在初始化时添加其他内容类型。
Grip.add_content_types()
clear_cache
清除下载的资源。
Grip.clear_cache()
render
渲染应用程序并返回通常在浏览器中访问时出现的HTML unicode。
Grip.render(route=None)
route
: 要渲染的路由,默认为/
run
启动服务器以渲染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
: 服务器启动时打开浏览器到该地址
class AlreadyRunningError(RuntimeError)
当服务器已在运行时调用Grip.run
时引发。
AlreadyRunningError()
class ReadmeNotFoundError(NotFoundError or IOError)
当找不到指定的Readme时引发。
ReadmeNotFoundError(path=None, message=None)
class ReadmeAssetManager(object)
管理与Readme页面一起渲染的样式和字体资源。这是一个抽象基类。
ReadmeAssetManager(cache_path, style_urls=None)
class GitHubAssetManager(ReadmeAssetManager)
管理与Readme页面一起渲染的样式和字体资源。将cache_path设置为None以禁用缓存。
class ReadmeReader(object)
从URL子路径读取Readme内容。这是一个抽象基类。
ReadmeReader()
class DirectoryReader(ReadmeReader)
从URL子路径读取Readme文件。
DirectoryReader(path=None, silent=False)
class TextReader(ReadmeReader)
从提供的unicode字符串读取Readme内容。
TextReader(text, display_filename=None)
class StdinReader(TextReader)
从标准输入读取Readme文本。
StdinReader(display_filename=None)
class ReadmeRenderer(object)
渲染Readme。这是一个抽象基类。
ReadmeRenderer(user_content=None, context=None)
class GitHubRenderer(ReadmeRenderer)
使用GitHub Markdown API渲染指定的Readme。
GitHubRenderer(user_content=None, context=None, api_url=None, raw=None)
class 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
环境变量,则使用此常量作为默认值。
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"
贡献
- 查看未解决的问题或开一个新的问题,讨论您的功能想法或发现的bug
- 复刻仓库并进行修改
- 开启一个新的拉取请求
如果您的PR等待了一段时间,随时可以[在Twitter上ping我][twitter]。