- Notifications
You must be signed in to change notification settings - Fork436
Preview GitHub README.md files locally before committing them.
License
joeyespo/grip
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Render local readme files before sending off to GitHub.
Grip is a command-line server application written in Python that uses theGitHub markdown API to render a local readme file. The stylesand rendering come directly from GitHub, so you'll know exactly how it will appear.Changes you make to the Readme will be instantly reflected in the browser withoutrequiring a page refresh.
Sometimes you just want to see the exact readmeresult before committing and pushing to GitHub.
Especially when doingReadme-driven development.
To install grip, simply:
$pip install gripOn OS X, you can also install with Homebrew:
$brew install gripTo render the readme of a repository:
$cd myrepo$grip * Running on http://localhost:6419/
Now open a browser and visithttp://localhost:6419.Or run with-b and Grip will open a new browser tab for you.
You can also specify a port:
$grip 80 * Running on http://localhost:80/
Or an explicit file:
$grip AUTHORS.md * Running on http://localhost:6419/
Alternatively, you could just rungrip and visitlocalhost:6419/AUTHORS.mdsince grip supports relative URLs.
You can combine the previous examples. Or specify a hostname instead of a port. Or provide both.
$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/
You can even bypass the server andexport to a single HTML file, with all the styles and assets inlined:
$grip --exportExporting to README.html
Control the output name with the second argument:
$grip README.md --export index.htmlExporting to index.html
If you're exporting a bunch of files, you can prevent styles from being inlining to save space with--no-inline:
$grip README.md --export --no-inline introduction.htmlExporting to introduction.html
Reading and writing fromstdin andstdout is also supported, allowing you to use Grip with other programs:
$cat README.md| grip - * Running on http://localhost:6419/
$grip AUTHORS.md --export -| bcat$cat README.md| grip --export -| lessThis allows you to quickly test how things look by entering Markdown directly in your terminal:
$grip -Hello **world**!^D * Running on http://localhost:6419/
Note:^D meansCtrl+D, which works on Linux and OS X. On Windows you'll have to useCtrl+Z.
Rendering as user-content likecomments andissues is also supported, with an optional repository context for linking to issues:
$grip --user-content --context=joeyespo/grip * Running on http://localhost:6419/
For more details and additional options, see the help:
$grip -hGrip strives to be as close to GitHub as possible. To accomplish this, gripusesGitHub's Markdown API so that changes to their renderingengine are reflected immediately without requiring you to upgrade grip.However, because of this you may hit the API's hourly rate limit. If thishappens, grip offers a way to access the API using your credentialsto unlock a much higher rate limit.
$grip --user<your-username> --pass<your-password>Or use apersonal access token with an empty scope (note that a token isrequired if your GitHub account is set up with two-factor authentication):
$grip --pass<token>You can persist these optionsin your local configuration.For security purposes, it's highly recommended that youuse an access tokenover a password. (You could also keep your password safe by configuringGrip tograb your password from a password manager.)
There's also awork-in-progress branch to provideoffline rendering. Once this resembles GitHub more precisely, it'llbe exposed in the CLI, and will ultimately be used as a seamless fallbackengine for when the API can't be accessed.
Grip always accesses GitHub over HTTPS,so your README and credentials are protected.
Here's how others from the community are using Grip.
Want to share your own?Say hello @joeyespo orsubmit a pull request.
$git clone https://github.com/YOUR_USERNAME/YOUR_REPOSITORY.wiki.git$cd YOUR_REPOSITORY.wiki$grip
Enter the directory:
$cd YOUR_DIR$export GRIPURL=$(pwd)
Include all assets by setting the
CACHE_DIRECTORYconfig variable:$echo"CACHE_DIRECTORY = '$(pwd)/assets'">>~/.grip/settings.pyExport all your Markdown files with Grip and replace absolute asset paths with relative paths:
$forfin*.md;do grip --export$f --no-inline;done$forfin*.html;do sed -i''"s?$GRIPURL/??g"$f;done
You can optionally compress the set of HTML files todocs.tgz with:
$tar -czvf docs.tgz`ls| grep [\.]html$` assetsLooking for a cross platform solution? Here's an equivalentPython script.
To customize Grip, create~/.grip/settings.py, then add one or more of the following variables:
HOST: The host to use when not provided as a CLI argument,localhostby defaultPORT: The port to use when not provided as a CLI argument,6419by defaultDEBUG: Whether to use Flask's debugger when an error happens,Falseby defaultDEBUG_GRIP: Prints extended information when an error happens,Falseby defaultAPI_URL: Base URL for the github API, for example that of a Github Enterprise instance.https://api.github.comby defaultCACHE_DIRECTORY: The directory, relative to~/.grip, to place cached assets (this gets run through the following filter:CACHE_DIRECTORY.format(version=__version__)),'cache-{version}'by defaultAUTOREFRESH: Whether to automatically refresh the Readme content when the file changes,Trueby defaultQUIET: Do not print extended information,Falseby defaultSTYLE_URLS: Additional URLs that will be added to the rendered page,[]by defaultUSERNAME: The username to use when not provided as a CLI argument,Noneby defaultPASSWORD: The password orpersonal access token to use when not provided as a CLI argument (Please don't save your passwords here. Instead, use an access token or drop in this codegrab your password from a password manager),Noneby default
Note that this is a Python file. If you see'X' is not defined errors, youmay have overlooked some quotes. For example:
USERNAME='your-username'PASSWORD='your-personal-access-token'
GRIPHOME: Specify an alternativesettings.pylocation,~/.gripby defaultGRIPURL: The URL of the Grip server,/__/gripby default
This file is a normal Python script, so you can add more advanced configuration.
For example, to read a setting from the environment and provide a default valuewhen it's not set:
PORT=os.environ.get('GRIP_PORT',8080)
You can access the API directly with Python, using it in your own projects:
fromgripimportserveserve(port=8080)*Runningonhttp://localhost:8080/
Run main directly:
fromgripimportmainmain(argv=['-b','8080'])*Runningonhttp://localhost:8080/
Or access the underlying Flask application for even more flexibility:
fromgripimportcreate_appgrip_app=create_app(user_content=True)# Use in your own app
Runs a local server and renders the Readme file locatedatpath when visited in the browser.
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: The filename to render, or the directory containing your Readme file, defaulting to the current working directoryhost: The host to listen on, defaulting to the HOST configuration variableport: The port to listen on, defaulting to the PORT configuration variableuser_content: Whether to render a document asuser-content like user comments or issuescontext: The project context to use whenuser_contentis true, whichtakes the form ofusername/projectusername: The user to authenticate with GitHub to extend the API limitpassword: The password to authenticate with GitHub to extend the API limitrender_offline: Whether to render locally usingPython-Markdown (Note: this is a work in progress)render_wide: Whether to render a wide page,Falseby default (this has no effect when used withuser_content)render_inline: Whether to inline the styles within the HTML fileapi_url: A different base URL for the github API, for example that of a Github Enterprise instance. The default is the public APIhttps://api.github.com.title: The page title, derived frompathby defaultautorefresh: Automatically update the rendered content when the Readme file changes,Trueby defaultbrowser: Open a tab in the browser after the server starts.,Falseby defaultgrip_class: Use a customGrip class
Writes the specified Readme file to an HTML file with styles and assets inlined.
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: The filename to render, or the directory containing your Readme file, defaulting to the current working directoryuser_content: Whether to render a document asuser-content like user comments or issuescontext: The project context to use whenuser_contentis true, whichtakes the form ofusername/projectusername: The user to authenticate with GitHub to extend the API limitpassword: The password to authenticate with GitHub to extend the API limitrender_offline: Whether to render locally usingPython-Markdown (Note: this is a work in progress)render_wide: Whether to render a wide page,Falseby default (this has no effect when used withuser_content)render_inline: Whether to inline the styles within the HTML file (Note: unlike the other API functions, this defaults toTrue)out_filename: The filename to write to,<in_filename>.htmlby defaultapi_url: A different base URL for the github API, for example that of a Github Enterprise instance. The default is the public APIhttps://api.github.com.title: The page title, derived frompathby defaultquiet: Do not print to the terminaltheme: Theme to view markdown file (light mode or dark mode). Valid options ("light", "dark"). Default: "light".grip_class: Use a customGrip class
Creates a Flask application you can use to render and serve the Readme files.This is the same app used byserve andexport and initializes the cache,using the cached styles when available.
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: The filename to render, or the directory containing your Readme file, defaulting to the current working directoryuser_content: Whether to render a document asuser-content like user comments or issuescontext: The project context to use whenuser_contentis true, whichtakes the form ofusername/projectusername: The user to authenticate with GitHub to extend the API limitpassword: The password to authenticate with GitHub to extend the API limitrender_offline: Whether to render locally usingPython-Markdown (Note: this is a work in progress)render_wide: Whether to render a wide page,Falseby default (this has no effect when used withuser_content)render_inline: Whether to inline the styles within the HTML fileapi_url: A different base URL for the github API, for example that of a Github Enterprise instance. The default is the public APIhttps://api.github.com.title: The page title, derived frompathby defaulttext: A string or stream of Markdown text to render instead of being loaded frompath(Note:pathcan be used to set the page title)grip_class: Use a customGrip class
Renders the application created bycreate_app and returns the HTML that wouldnormally appear when visiting that route.
render_app(app,route='/')
app: The Flask application to renderroute: The route to render, '/' by default
Renders the specified markdown text without caching.
render_content(text,user_content=False,context=None,username=None,password=None,render_offline=False,api_url=None,title=None)
text: The Markdown text to renderuser_content: Whether to render a document asuser-content like user comments or issuescontext: The project context to use whenuser_contentis true, whichtakes the form ofusername/projectusername: The user to authenticate with GitHub to extend the API limitpassword: The password to authenticate with GitHub to extend the API limitrender_offline: Whether to render locally usingPython-Markdown (Note: this is a work in progress)api_url: A different base URL for the github API, for example that of a Github Enterprise instance. This is required when not using the offline renderer.title: The page title, derived frompathby default
Renders the markdown from the specified path or text, without caching,and returns an HTML page that resembles the GitHub Readme view.
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: The path to use for the page title, rendering'README.md'if Noneuser_content: Whether to render a document asuser-content like user comments or issuescontext: The project context to use whenuser_contentis true, whichtakes the form ofusername/projectusername: The user to authenticate with GitHub to extend the API limitpassword: The password to authenticate with GitHub to extend the API limitrender_offline: Whether to render offline usingPython-Markdown (Note: this is a work in progress)render_wide: Whether to render a wide page,Falseby default (this has no effect when used withuser_content)render_inline: Whether to inline the styles within the HTML fileapi_url: A different base URL for the github API, for example that of a Github Enterprise instance. The default is the public APIhttps://api.github.com.title: The page title, derived frompathby defaulttext: A string or stream of Markdown text to render instead of being loaded frompath(Note:pathcan be used to set the page title)quiet: Do not print to the terminaltheme: Theme to view markdown file (light mode or dark mode). Valid options ("light", "dark"). Default: "light".grip_class: Use a customGrip class
Clears the cached styles and assets.
clear_cache(grip_class=None)
Runs Grip with the specified arguments.
main(argv=None,force_utf8=True)
argv: The arguments to run with,sys.argv[1:]by defaultforce_utf8: Sets the default encoding toutf-8in the current Python instance. This has no effect on Python 3 since Unicode is handled by default
A Flask application that can serve a file or directory containing a README.
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)
Returns the default renderer using the current config. This is only used ifrenderer is set to None in the constructor.
Grip.default_renderer()
Returns the default asset manager using the current config. This is only usedif asset_manager is set to None in the constructor.
Grip.default_asset_manager()
Adds the application/x-font-woff and application/octet-stream content types ifthey are missing. Override to add additional content types on initialization.
Grip.add_content_types()
Clears the downloaded assets.
Grip.clear_cache()
Renders the application and returns the HTML unicode that would normally appearwhen visiting in the browser.
Grip.render(route=None)
route: The route to render,/by default
Starts a server to render the README. This callsFlask.run internally.
Grip.run(host=None,port=None,debug=None,use_reloader=None,open_browser=False)
host: The hostname to listen on. Set this to'0.0.0.0'to have the serveravailable externally as well,'localhost'by defaultport: The port of the webserver. Defaults to6419debug: If given, enable or disable debug mode. SeeFlask.debug.use_reloader: Should the server automatically restart the python processif modules were changed?Falseby default unless theDEBUG_GRIPsetting is specified.open_browser: Opens the browser to the address when the server starts
Raised whenGrip.run is called while the server is already running.
AlreadyRunningError()Raised when the specified Readme could not be found.
ReadmeNotFoundError(path=None,message=None)
Manages the style and font assets rendered with Readme pages. This is anabstract base class.
ReadmeAssetManager(cache_path,style_urls=None)
Manages the style and font assets rendered with Readme pages. Set cache_path toNone to disable caching.
Reads Readme content from a URL subpath. This is an abstract base class.
ReadmeReader()Reads Readme files from URL subpaths.
DirectoryReader(path=None,silent=False)
Reads Readme content from the provided unicode string.
TextReader(text,display_filename=None)
Reads Readme text from STDIN.
StdinReader(display_filename=None)
Renders the Readme. This is an abstract base class.
ReadmeRenderer(user_content=None,context=None)
Renders the specified Readme using the GitHub Markdown API.
GitHubRenderer(user_content=None,context=None,api_url=None,raw=None)
Renders the specified Readme locally using pure Python. Note: This is currentlyan incomplete feature.
OfflineRenderer(user_content=None,context=None)
The common Markdown file titles on GitHub.
SUPPORTED_TITLES= ['README','Home']
filename: The UTF-8 file to read.
The supported extensions, as defined byGitHub.
SUPPORTED_EXTENSIONS= ['.md','.markdown']
This constant contains the names Grip looks for when no file is provided.
DEFAULT_FILENAMES= [title+extfortitleinSUPPORTED_TITLESforextinSUPPORTED_EXTENSIONS]
This constant contains the default Readme filename, namely:
DEFAULT_FILENAME=DEFAULT_FILENAMES[0]# README.md
This constant points to the default value if theGRIPHOMEenvironment variable is not specified.
DEFAULT_GRIPHOME='~/.grip'
The default URL of the Grip server and all its assets:
DEFAULT_GRIPURL='/__/grip'
The default app_url value:
DEFAULT_API_URL='https://api.github.com'
Install the package and test requirements:
$pip install -e .[tests]Run tests withpytest:
$pytestOr to re-run tests as you make changes, usepytest-watch:
$ptwIf you're experiencing a problem with Grip, it's likely that an assumption madeabout the GitHub API has been broken. To verify this, run:
$pytest -m assumptionSince the external assumptions rely on an internet connection, you may want to skipthem when developing locally. Tighten the cycle even further by stopping on thefirst failure with-x:
$pytest -xm"not assumption"Or withpytest-watch:
$ptw -- -xm"not assumption"- Check the open issues or open a new issue to start a discussion aroundyour feature idea or the bug you found
- Fork the repository and make your changes
- Open a new pull request
If your PR has been waiting a while, feel free toping me on Twitter.
About
Preview GitHub README.md files locally before committing them.
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.