├── .gitignore ├── LICENSE ├── Readme.md ├── bin └── coral ├── completions ├── coral.bash └── coral.zsh ├── libexec ├── coral ├── coral-binstubs ├── coral-bundle-ack ├── coral-bundle-spec-read ├── coral-checkout ├── coral-clone ├── coral-commands ├── coral-completions ├── coral-doctor ├── coral-fetch-url ├── coral-filter-gh-project ├── coral-find-readme ├── coral-gem-browse ├── coral-gem-browse-project ├── coral-gem-clone ├── coral-gem-dir ├── coral-gem-open ├── coral-gem-project-url ├── coral-gem-remote-info ├── coral-gem-spec-read ├── coral-gem-standalone ├── coral-gh-project-search ├── coral-git-semver-tags ├── coral-git-workdir ├── coral-github-markup ├── coral-help ├── coral-init ├── coral-list ├── coral-list-checkouts ├── coral-open-dir ├── coral-parse-json ├── coral-path ├── coral-render-markup ├── coral-set-canonical ├── coral-sh-cd └── coral-sort-versions └── resource └── readme.css /.gitignore: -------------------------------------------------------------------------------- 1 | /repos 2 | /bin/* 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 Mislav Marohnić 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so, 8 | subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 15 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 16 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 17 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 18 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Coral 2 | ===== 3 | 4 | Coral is a set of shell commands that help working with open-source projects, 5 | Ruby apps, GitHub and more. 6 | 7 | Coral is a [sub][] in the way its structure grew out of rbenv's. 8 | 9 | Installation 10 | ------------ 11 | 12 | ~~~ sh 13 | git clone git://github.com/mislav/coral.git ~/.coral 14 | 15 | # display instructions how to edit shell configuration: 16 | ~/.coral/bin/coral init 17 | 18 | # later, when you've reloaded the new shell configuration: 19 | coral doctor 20 | ~~~ 21 | 22 | See `coral help` for available commands. 23 | 24 | 25 | Features 26 | -------- 27 | 28 | ### Organizing git clones of GitHub repos 29 | 30 | If you read the code or contribute to a number of open-source projects, you 31 | might have become tired of picking a clone destination for each of those 32 | projects. 33 | 34 | Coral can clone repos for you and keep them organized in its internal directory 35 | structure: 36 | 37 | ~~~ sh 38 | coral clone bootstrap # will search GitHub and pick the first result 39 | coral clone twitter/bootstrap # if you want to be explicit 40 | coral path bootstrap # display the path of where Bootstrap is locally 41 | 42 | coral cd bootstrap # cd into Bootstrap's project directory 43 | git tag | coral sort-versions # display available versions 44 | 45 | coral checkout bootstrap v2.2.0 # check out a new working copy for Bootstrap v2.2.0 46 | coral cd bootstrap@v2.2.0 # cd into the new working copy 47 | 48 | coral list bootstrap # list available working copies of Bootstrap 49 | ~~~ 50 | 51 | ### Working with RubyGems and Bundler 52 | 53 | The bonus of all Coral gem commands are that they are Bundler-aware; i.e. when 54 | you give them a name of the gem, they first search for it in the current bundle 55 | and then in globally installed gems. This helps you with inspecting versions of 56 | gems that your project currently uses. 57 | 58 | ~~~ sh 59 | coral gem-dir activesupport # print the root directory of a gem 60 | coral gem-open activesupport # open gem's source code in the editor 61 | coral gem-browse sinatra # open Sinatra's home page in the browser 62 | coral gem-browse-project sinatra # open Sinatra's project page on Github 63 | 64 | coral bundle-ack -w redirect_to # search across all gems in the bundle 65 | ~~~ 66 | 67 | ### Render documentation files to HTML 68 | 69 | ~~~ sh 70 | # render any markup the same way GitHub.com does HTML: 71 | coral github-markup path/to/README.md >> readme.html 72 | 73 | # `github-markup` output + styles & syntax highlighting; pipe to browser 74 | coral render-markup path/to/README.md | bcat 75 | ~~~ 76 | 77 | ### Opening source code in editor 78 | 79 | Handy shortcuts for opening a project's directory in your favorite text editor. 80 | The `gem-open` command also has a nice feature for preloading the README file 81 | for you, if it exists: 82 | 83 | ~~~ sh 84 | coral gem-open activesupport # open gem's source code & README in the editor 85 | coral open-dir path/to/project # open a project in $EDITOR 86 | ~~~ 87 | 88 | You should have your shell configured like so: 89 | 90 | * Vim: 91 | 92 | EDITOR=vim 93 | GEM_EDITOR=mvim # or "gvim" on Linux 94 | 95 | * Sublime Text 2: 96 | 97 | EDITOR='subl -w' 98 | GEM_EDITOR=subl 99 | 100 | * TextMate: 101 | 102 | EDITOR='mate -w' 103 | GEM_EDITOR=mate 104 | 105 | ### Miscellaneous unix goodies 106 | 107 | ~~~ sh 108 | coral sort-versions # sort version numbers on STDIN 109 | coral fetch-url # fetch URL and cache the result for 24 hours 110 | coral parse-json # parse JSON and output in flat, line-based format 111 | ~~~ 112 | 113 | 114 | [sub]: https://github.com/37signals/sub 115 | -------------------------------------------------------------------------------- /bin/coral: -------------------------------------------------------------------------------- 1 | ../libexec/coral -------------------------------------------------------------------------------- /completions/coral.bash: -------------------------------------------------------------------------------- 1 | _coral() { 2 | COMPREPLY=() 3 | local word="${COMP_WORDS[COMP_CWORD]}" 4 | 5 | if [ "$COMP_CWORD" -eq 1 ]; then 6 | COMPREPLY=( $(compgen -W "$(coral commands)" -- "$word") ) 7 | else 8 | local command="${COMP_WORDS[1]}" 9 | local completions="$(coral completions "$command")" 10 | COMPREPLY=( $(compgen -W "$completions" -- "$word") ) 11 | fi 12 | } 13 | 14 | complete -F _coral coral 15 | -------------------------------------------------------------------------------- /completions/coral.zsh: -------------------------------------------------------------------------------- 1 | if [[ ! -o interactive ]]; then 2 | return 3 | fi 4 | 5 | compctl -K _coral coral 6 | 7 | _coral() { 8 | local word words completions 9 | read -cA words 10 | word="${words[2]}" 11 | 12 | if [ "${#words}" -eq 2 ]; then 13 | completions="$(coral commands)" 14 | else 15 | completions="$(coral completions "${word}")" 16 | fi 17 | 18 | reply=("${(ps:\n:)completions}") 19 | } 20 | -------------------------------------------------------------------------------- /libexec/coral: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | [ -n "$CORAL_DEBUG" ] && set -x 4 | 5 | # yanked from rbenv 6 | resolve_link() { 7 | $(type -p greadlink readlink | head -1) "$1" 8 | } 9 | abs_dirname() { 10 | local cwd="$(pwd)" 11 | local path="$1" 12 | 13 | while [ -n "$path" ]; do 14 | cd "${path%/*}" 15 | local name="${path##*/}" 16 | path="$(resolve_link "$name" || true)" 17 | done 18 | 19 | pwd 20 | cd "$cwd" 21 | } 22 | 23 | if [ -z "${CORAL_ROOT}" ]; then 24 | CORAL_ROOT="${HOME}/.coral" 25 | else 26 | CORAL_ROOT="${CORAL_ROOT%/}" 27 | fi 28 | export CORAL_ROOT 29 | 30 | libexec_path="$(abs_dirname "$0")" 31 | export PATH="${libexec_path}:${PATH}" 32 | 33 | command="$1" 34 | case "$command" in 35 | "" | "-h" | "--help" ) 36 | exec coral-help 37 | ;; 38 | * ) 39 | command_path="$(command -v "coral-$command" || true)" 40 | if [ ! -x "$command_path" ]; then 41 | echo "${0}: no such command \`$command'" >&2 42 | exit 1 43 | fi 44 | 45 | shift 1 46 | 47 | if [ $# -eq 1 ] && [ "$1" = '-h' -o "$1" = '--help' ]; then 48 | exec coral-help "$command" 49 | else 50 | exec "$command_path" "$@" 51 | fi 52 | ;; 53 | esac 54 | -------------------------------------------------------------------------------- /libexec/coral-binstubs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral binstub [-f | --force] [@] 3 | # Summary: Install binstubs for a repo to $CORAL_ROOT/bin 4 | # Help: Installs binstubs for the specified repo to $CORAL_ROOT/bin, which 5 | # should be already present in your $PATH. This enables accessing executables 6 | # from locally cloned projects. 7 | 8 | set -e 9 | [ -n "$CORAL_DEBUG" ] && set -x 10 | 11 | if [ "$1" = '--complete' ]; then 12 | shift 13 | exec coral-path --complete "$@" 14 | fi 15 | 16 | if [[ $1 = "--force" || $1 = "-f" ]]; then 17 | force=1 18 | shift 1 19 | fi 20 | 21 | repo="$(coral-path "$1")" 22 | repo_rel="..${repo#$CORAL_ROOT}" 23 | 24 | cd "$repo" 25 | 26 | for binfile in bin/*; do 27 | bindest="${CORAL_ROOT}/${binfile}" 28 | 29 | if [[ -n $force || ! -e $bindest ]]; then 30 | if head -1 "$binfile" | grep ruby >/dev/null; then 31 | { echo "#!/bin/sh" 32 | echo "dir=\"\$(cd \"\$(dirname \"\$0\")/${repo_rel}\" && pwd)\"" 33 | echo "RUBYLIB=\"\${dir}/lib:\$RUBYLIB\" exec -a ${binfile##*/} \"\${dir}/${binfile}\" \"\$@\"" 34 | } > "$bindest" 35 | chmod +x "$bindest" 36 | echo "installed $bindest" 37 | else 38 | ln -shfv "${repo_rel}/${binfile}" "$bindest" 39 | fi 40 | else 41 | echo "$bindest already exists" >&2 42 | exit 1 43 | fi 44 | done 45 | -------------------------------------------------------------------------------- /libexec/coral-bundle-ack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral bundle-ack [] 3 | # Summary: Search inside the current bundle 4 | # Help: Searches through source code of each gem in the bundle. Search is 5 | # powered by ack and all arguments are forwarded to it. By default, 6 | # is interpreted as a Perl-compatible regex. 7 | # 8 | # See `ack --help` 9 | 10 | set -e 11 | [ -n "$CORAL_DEBUG" ] && set -x 12 | 13 | dirs="$(coral-bundle-spec-read lib_dirs)" 14 | 15 | exec ack --ruby "$@" $dirs 16 | -------------------------------------------------------------------------------- /libexec/coral-bundle-spec-read: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Usage: coral bundle-spec-read [--with-name] [--with-version] 3 | # Summary: Display a property value for each gem in the bundle 4 | # Help: Displays the value returned by of each gemspec in the current 5 | # bundle. can be any instance method of Gem::Specification, or the 6 | # special value "lib_dirs" which lists all lib directories for each gem. 7 | # 8 | # When `--with-name/--with-version` are present, prepend the name/version of 9 | # each gem in the output separated by tab characters. 10 | # 11 | # Some useful methods are: 12 | # author(s) 13 | # email 14 | # summary, description 15 | # homepage 16 | # date 17 | 18 | require 'rubygems' 19 | require 'bundler' 20 | 21 | $\ = "\n" 22 | $, = "\t" 23 | with_name = ARGV.delete '--with-name' 24 | with_version = ARGV.delete '--with-version' 25 | method = ARGV[0] 26 | 27 | begin 28 | specs = Bundler.load.specs 29 | rescue 30 | abort $!.message 31 | else 32 | case method 33 | when 'lib_dirs' 34 | dirs = Dir.glob(specs.map {|spec| spec.lib_dirs_glob }) 35 | print dirs.join($\) 36 | else 37 | errors = [] 38 | specs.each { |spec| 39 | begin 40 | value = spec.send(method) 41 | rescue 42 | $stderr.puts $!.message 43 | exit 2 44 | else 45 | unless value.nil? or value.respond_to?(:empty?) && value.empty? 46 | values = [] 47 | values << spec.name if with_name 48 | values << spec.version if with_version 49 | 50 | if value.respond_to? :join 51 | value.each {|v| print(* values + [v.to_s]) } 52 | else 53 | values << value.to_s 54 | print(*values) 55 | end 56 | end 57 | end 58 | } 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /libexec/coral-checkout: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral checkout 3 | # Summary: Create a new working copy for a repo 4 | # Help: Creates a new working copy for a repository in Coral. The new repo is 5 | # named '@' and, altough it's a separate directory, it shares 6 | # the same git repo as the original working copy. This is achieved by magic. 7 | # 8 | # If corresponds to a git tag, branch, or remote, the new working 9 | # copy will be switched to the corresponding git revision. 10 | 11 | set -e 12 | [ -n "$CORAL_DEBUG" ] && set -x 13 | 14 | [ "$1" = "--complete" ] && exec coral-list --complete 15 | 16 | origin="$(coral-path "$1")" 17 | workdir="${CORAL_ROOT}/repos/${1%@*}@${2}" 18 | 19 | coral-git-workdir "$origin" "$workdir" >/dev/null 20 | 21 | cd "$workdir" 22 | 23 | if git rev-parse --quiet --verify "$2" >/dev/null; then 24 | git checkout --quiet "$2" 25 | elif git rev-parse --quiet --verify "origin/${2}" >/dev/null; then 26 | git checkout --track "origin/${2}" 27 | elif git remote | grep -x "$2" >/dev/null; then 28 | git checkout --track -b "$2" "${2}/master" 29 | else 30 | git checkout -f HEAD 31 | fi 32 | -------------------------------------------------------------------------------- /libexec/coral-clone: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral clone ( | / | ) 3 | # Summary: Clone a GitHub repo to a location in $CORAL_ROOT 4 | # Help: Clones a GitHub repository to an automatically assigned location under 5 | # $CORAL_ROOT ("~/.coral/" by default). 6 | # 7 | # To see where the repository is located, use `coral path `. 8 | 9 | set -e 10 | [ -n "$CORAL_DEBUG" ] && set -x 11 | 12 | name="${1?}" 13 | 14 | if [[ $name != */* ]]; then 15 | name="$(coral-gh-project-search "$name" | head -1)" 16 | if [[ -z $name ]]; then 17 | echo "searching GitHub.com for \`$1' yielded no results" >&2 18 | exit 1 19 | fi 20 | fi 21 | 22 | name_with_owner="$(echo "$name" | coral-filter-gh-project)" 23 | [[ -n $name_with_owner ]] || name_with_owner="$1" 24 | 25 | name="${name_with_owner#*/}" 26 | owner="${name_with_owner%/*}" 27 | 28 | repo_path="${CORAL_ROOT}/repos/${name}@${owner}" 29 | 30 | mkdir -p "$(dirname "$repo_path")" 31 | 32 | git clone -v git://github.com/${owner}/${name}.git "$repo_path" 33 | -------------------------------------------------------------------------------- /libexec/coral-commands: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral commands [-v] 3 | # Summary: List available commands 4 | # Help: Lists coral commands available on the system. 5 | # This searches the whole PATH for executables starting with "coral-". 6 | # 7 | # It outputs only the command names. To get full paths, use `-v`. 8 | 9 | set -e 10 | [ -n "$CORAL_DEBUG" ] && set -x 11 | 12 | unset verbose 13 | unset sh 14 | 15 | case "$1" in 16 | "-v" ) 17 | verbose=1 18 | shift 19 | ;; 20 | "--sh" ) 21 | sh=1 22 | shift 23 | ;; 24 | esac 25 | 26 | shopt -s nullglob 27 | 28 | { for path in ${PATH//:/$'\n'}; do 29 | for command in "${path}/coral-"*; do 30 | if [ -x "$command" ]; then 31 | [ -z $verbose ] && command=${command##*coral-} 32 | if [ -z "$sh" -o ${command:0:3} = "sh-" ]; then 33 | echo ${command#sh-} 34 | fi 35 | fi 36 | done 37 | done 38 | } | sort | uniq 39 | -------------------------------------------------------------------------------- /libexec/coral-completions: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | command="$1" 5 | if [ -z "$command" ]; then 6 | echo "usage: coral completions []" >&2 7 | exit 1 8 | fi 9 | 10 | command_path="$(command -v "coral-$command")" 11 | if grep -we --complete "$command_path" >/dev/null; then 12 | shift 13 | exec "$command_path" --complete "$@" 14 | fi 15 | -------------------------------------------------------------------------------- /libexec/coral-doctor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral doctor [-s] 3 | # Summary: Check dependencies for Coral utilities 4 | # Help: Checks that dependencies for various Coral utilities are satisfied. 5 | # Current dependencies include git, ack, curl, Bundler and several Ruby gems. 6 | # The exit status is zero only if all dependencies are satisfied. 7 | # 8 | # With the `-s` flag, this outputs a script which can be eval'ed in the shell 9 | # that installs the missing dependencies by means of Homebrew and RubyGems. 10 | 11 | set -e 12 | [ -n "$CORAL_DEBUG" ] && set -x 13 | 14 | [[ $1 = "-s" ]] && script=1 15 | retval=0 16 | brews="" 17 | gems="" 18 | 19 | say() { 20 | echo -e "$@" 21 | } 22 | 23 | fail() { 24 | if [[ -z $script ]] && [[ $# -ne 0 ]]; then 25 | say 26 | say $@ 27 | fi 28 | retval=1 29 | } 30 | 31 | missing() { 32 | if which "$1" >/dev/null; then 33 | return 1 34 | else 35 | fail "\`${1}' not found" 36 | return 0 37 | fi 38 | } 39 | 40 | gem_missing() { 41 | if ruby -rubygems -e "require '${2:-$1}'" 2>/dev/null; then 42 | return 1 43 | else 44 | fail "'${1}' gem not found" 45 | return 0 46 | fi 47 | } 48 | 49 | if ruby -r rubygems -e ''; then 50 | [[ -z $script ]] && say "Using Ruby:\n $(ruby -v)" 51 | else 52 | fail 53 | fi 54 | 55 | which rbenv >/dev/null && rbenv rehash 56 | 57 | if missing git; then 58 | brews="${brews} git" 59 | fi 60 | 61 | if missing ack; then 62 | brews="${brews} ack" 63 | fi 64 | 65 | if missing asciidoc; then 66 | brews="${brews} asciidoc" 67 | fi 68 | 69 | if missing curl; then 70 | brews="${brews} curl" 71 | fi 72 | 73 | if missing bundle; then 74 | gems="${gems} bundler" 75 | fi 76 | 77 | if gem_missing github-markup github/markup; then 78 | gems="${gems} github-markup" 79 | fi 80 | 81 | if gem_missing html-pipeline html/pipeline; then 82 | gems="${gems} html-pipeline" 83 | fi 84 | 85 | if gem_missing rdoc; then 86 | gems="${gems} rdoc" 87 | fi 88 | 89 | if gem_missing redcloth; then 90 | gems="${gems} RedCloth" 91 | fi 92 | 93 | if [[ $retval -ne 0 ]]; then 94 | if [[ -z $script ]]; then 95 | say 96 | say "You can install missing dependencies with:" 97 | say ' eval "$(coral doctor -s)"' 98 | else 99 | [[ -n $brews ]] && echo brew install${brews} 100 | [[ -n $gems ]] && echo gem install --no-rdoc --no-ri${gems} 101 | exit 0 102 | fi 103 | fi 104 | 105 | exit $retval 106 | -------------------------------------------------------------------------------- /libexec/coral-fetch-url: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral fetch-url ( [] | --clear-cache) 3 | # Summary: Fetch contents from a URL and cache the result 4 | # Help: Fetches contents from a URL and caches the result for 24 hours in 5 | # "$TMPDIR/coral" (default: "/tmp/coral"). 6 | # 7 | # If `--clear-cache` is used instead of URL, this command will purge the cache, 8 | # causing all new fetches to actually hit the server. 9 | 10 | set -e 11 | [ -n "$CORAL_DEBUG" ] && set -x 12 | 13 | TMPDIR=${TMPDIR:-/tmp} 14 | 15 | if [[ $1 = --clear-cache ]]; then 16 | rm -rf "${TMPDIR%/}/coral" 17 | exit 18 | fi 19 | 20 | url="${1?}" 21 | shift 1 22 | 23 | cache="${TMPDIR%/}/coral/${url#*://}" 24 | expires="60 * 60 * 24" 25 | 26 | if [[ $(date +%s) -lt "$(stat -qf %m "$cache") + $expires" ]]; then 27 | cat "$cache" 28 | else 29 | mkdir -p "$(dirname "$cache")" 30 | curl -fsSLG "$url" "$@" | tee "$cache" 31 | fi 32 | -------------------------------------------------------------------------------- /libexec/coral-filter-gh-project: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Usage: coral filter-gh-project 3 | # Summary: Extract owner & repo information from GitHub URLs 4 | # Help: Scans STDIN for GitHub URLs, extracts the owner & repo information 5 | # from them and outputs that information in "owner/repo-name" format. 6 | # 7 | # Supports the following URL formats: 8 | # - http://github.com/mislav/coral/... 9 | # - http://mislav.github.com/coral/... 10 | # - git://github.com/mislav/coral.git 11 | # - git@github.com:mislav/coral.git 12 | 13 | name = /\w[\w.-]*/ 14 | owner = /[a-zA-Z0-9-]+/ 15 | url1 = %r{ :// (?:wiki\.)? github\.com / (#{owner}) / (#{name}) }x 16 | url2 = %r{ @ github\.com : (#{owner}) / (#{name}) }x 17 | url3 = %r{ :// (#{owner}) \. github\.com / (#{name}) }x 18 | 19 | while gets 20 | case $_ 21 | when url1, url2 22 | puts "#$1/" + $2.sub(/\.git$/, '') 23 | when url3 24 | puts "#$1/#$2" 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /libexec/coral-find-readme: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Usage: coral find-readme 3 | # Summary: Find README files in directory 4 | # Help: Finds files named "README*" in the given directory. 5 | # 6 | # Does not recurse into nested directories. 7 | 8 | find "$@" -maxdepth 1 -iname 'readme*' | sort 9 | -------------------------------------------------------------------------------- /libexec/coral-gem-browse: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral gem-browse 3 | # Summary: Open gem's home page in the browser 4 | 5 | set -e 6 | [ -n "$CORAL_DEBUG" ] && set -x 7 | 8 | homepage="$(coral-gem-spec-read homepage "$1")" 9 | 10 | exec git web--browse $homepage 11 | -------------------------------------------------------------------------------- /libexec/coral-gem-browse-project: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral gem-browse-project [] 3 | # Summary: Open GitHub project page for a gem 4 | # Help: Opens the gem's GitHub project page in the web browser. 5 | # 6 | # The can be, for example: 7 | # - issues 8 | # - pulls 9 | # - wiki 10 | # - commits 11 | # 12 | # Uses the `git web--browse` command. 13 | 14 | set -e 15 | [ -n "$CORAL_DEBUG" ] && set -x 16 | 17 | url="$(coral-gem-project-url "$1")" 18 | 19 | # add subpage 20 | [ -n "$2" ] && url=${url%/}/${2#/} 21 | 22 | exec git web--browse $url 23 | -------------------------------------------------------------------------------- /libexec/coral-gem-clone: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral gem-clone [] 3 | # Summary: Clone a GitHub project for a gem 4 | # Help: Attempts to clone gem's source code from GitHub. 5 | 6 | set -e 7 | [ -n "$CORAL_DEBUG" ] && set -x 8 | 9 | url="$(coral-gem-project-url "$1" git)" 10 | shift 1 11 | 12 | exec git clone $url "$@" 13 | -------------------------------------------------------------------------------- /libexec/coral-gem-dir: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral gem-dir [] 3 | # Summary: Print the root directory of a gem 4 | # Help: Outputs the filesystem path for a gem's root directory. 5 | # 6 | # If there is a Gemfile and there is no version constraint, the gem is searched 7 | # for in the current bundle first. 8 | 9 | set -e 10 | [ -n "$CORAL_DEBUG" ] && set -x 11 | 12 | if [ $# -lt 2 ] && [ -n "$BUNDLE_GEMFILE" -o -f Gemfile ]; then 13 | dir="$(bundle show "$1" --no-color 2>/dev/null || true)" 14 | fi 15 | 16 | if [ -d "$dir" ]; then 17 | echo "$dir" 18 | else 19 | coral-gem-spec-read full_gem_path "$@" 20 | fi 21 | -------------------------------------------------------------------------------- /libexec/coral-gem-open: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral gem-open [] 3 | # Summary: Open gem's source code in the text editor 4 | # Help: Opens a gem's source code in the text editor. If there is a Gemfile and 5 | # there is no version constraint, first tries to locate the gem as part of the 6 | # bundle. 7 | # 8 | # If there is a README file as part of the gem, it will be opened initially. 9 | # 10 | # The editor program is read from $GEM_EDITOR, then $VISUAL, then $EDITOR. 11 | 12 | set -e 13 | [ -n "$CORAL_DEBUG" ] && set -x 14 | 15 | dir="$(coral-gem-dir "$@")" 16 | readme="$(coral-find-readme "$dir" | head -1)" 17 | 18 | EDITOR="${VISUAL:-$EDITOR}" 19 | EDITOR="${GEM_EDITOR:-$EDITOR}" 20 | export EDITOR 21 | 22 | if [[ -z $readme ]]; then 23 | exec coral-open-dir "$dir" 24 | else 25 | exec coral-open-dir "$dir" "$readme" 26 | fi 27 | -------------------------------------------------------------------------------- /libexec/coral-gem-project-url: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral gem-project-url [] 3 | # Summary: Print the GitHub URL for a gem 4 | # Help: Print the URL of a gem's GitHub project. 5 | # 6 | # can be one of "git" or "ssh". 7 | 8 | set -e 9 | [ -n "$CORAL_DEBUG" ] && set -x 10 | 11 | # try if the local gem's homepage is a GitHub URL 12 | project="$(coral-gem-spec-read homepage "$1" 2>/dev/null | coral-filter-gh-project | head -1)" 13 | 14 | if [ -z "$project" ]; then 15 | # fetch the gem's remote info to scan the URLs 16 | project="$(coral-gem-remote-info "$1" | grep -E '^[a-z_]+_uri' | coral-filter-gh-project | head -1)" 17 | fi 18 | 19 | if [ -z "$project" ]; then 20 | echo "GitHub project not found." >&2 21 | exit 1 22 | else 23 | case "$2" in 24 | "git" ) 25 | url=git://github.com/${project}.git 26 | ;; 27 | "ssh" ) 28 | url=git@github.com:${project}.git 29 | ;; 30 | * ) 31 | url=https://github.com/${project} 32 | ;; 33 | esac 34 | 35 | echo $url 36 | fi 37 | -------------------------------------------------------------------------------- /libexec/coral-gem-remote-info: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Usage: coral gem-remote-info 3 | # Summary: Print metadata for a gem 4 | # Help: Outputs data from a remote gemspec. 5 | # 6 | # The format of the data is tab-separated key-value pairs. 7 | 8 | url="http://rubygems.org/api/v1/gems/${1}.json" 9 | 10 | coral-fetch-url "$url" | coral-parse-json 11 | -------------------------------------------------------------------------------- /libexec/coral-gem-spec-read: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Usage: coral gem-spec-read [] 3 | # Summary: Print information from a gemspec 4 | # Help: Prints the value returned by of a gem specification. 5 | # 6 | # Error codes: 7 | # 2: gem not found 8 | # 3: multiple gems matched the name pattern 9 | # 4: the field value to read is empty 10 | 11 | require 'rubygems' 12 | 13 | method, name, ver = ARGV 14 | ver ||= ">= 0" 15 | 16 | specs = Gem::Specification.find_all {|s| s.name == name } 17 | 18 | if specs.empty? 19 | re = /#{name}/ 20 | specs = Gem::Specification.find_all {|s| s.name =~ re } 21 | names = specs.map {|s| s.name }.uniq 22 | 23 | if names.size > 1 24 | $stderr.puts "multiple gems match: #{names.sort.inspect}" 25 | exit 3 26 | end 27 | end 28 | 29 | req = Gem::Version::Requirement.new ver 30 | 31 | if found = specs.find_all {|s| req.satisfied_by? s.version }.sort_by {|s| s.version }.last 32 | value = found.send(method) 33 | 34 | if value.nil? or value.respond_to?(:empty) && value.empty? 35 | $stderr.puts "#{method} is blank" 36 | exit 4 37 | else 38 | puts value 39 | end 40 | else 41 | $stderr.puts "gem not found" 42 | exit 2 43 | end 44 | -------------------------------------------------------------------------------- /libexec/coral-gem-standalone: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral gem-standalone [] 3 | # Summary: Install a Ruby gem as a standalone utility 4 | # Help: Installs a Ruby gem into $prefix ("/usr/local" by default) using 5 | # Bundler to make its runtime self-contained, and putting its executables into 6 | # "$prefix/bin". Running the installed executables doesn't require RubyGems or 7 | # Bundler anymore. 8 | # 9 | # This is useful for gems that are command-line tools, but the overhead of 10 | # RubyGems and activating its dependencies slows it down too much. 11 | 12 | set -e 13 | [ -n "$CORAL_DEBUG" ] && set -x 14 | 15 | gem_name="${1?}" 16 | prefix="${prefix:-/usr/local}" 17 | 18 | gemspec="$(coral-gem-spec-read loaded_from "$@")" 19 | gem_dir="$(coral-gem-dir "$@")" 20 | 21 | ruby_bin="$(ruby -r rbconfig -e ' 22 | rb = RbConfig::CONFIG 23 | puts File.join(rb["bindir"], rb["ruby_install_name"]) 24 | ')" 25 | 26 | mkdir -p "${prefix}/lib/${gem_name}" 27 | cd "${prefix}/lib/${gem_name}" 28 | 29 | cp -R "$gem_dir"/lib/ . 30 | cp "$gemspec" . 31 | 32 | echo "source :rubygems; gemspec" > Gemfile 33 | 34 | bundle install --path vendor --without development:test --standalone 35 | 36 | for binfile in "${gem_dir}/bin/"*; do 37 | if head -1 "$binfile" | grep ruby >/dev/null; then 38 | destfile="${prefix}/bin/$(basename "$binfile")" 39 | 40 | { echo "#!${ruby_bin} -r $(pwd)/vendor/bundler/setup" 41 | echo "\$LOAD_PATH[0] = '$(pwd)'" 42 | cat "$binfile" 43 | } > "$destfile" 44 | 45 | chmod +x "$destfile" 46 | fi 47 | done 48 | -------------------------------------------------------------------------------- /libexec/coral-gh-project-search: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral gh-project-search 3 | # Summary: Get a clone URL by searching GitHub 4 | 5 | set -e 6 | [ -n "$CORAL_DEBUG" ] && set -x 7 | 8 | # http://developer.github.com/v3/search/#search-repositories 9 | url="https://api.github.com/legacy/repos/search/${1}" 10 | 11 | # jump through hoops because the "url" field is strangely missing 12 | owner_and_name="$( 13 | coral-fetch-url "$url" | coral-parse-json | \ 14 | grep -E '\.(owner|name)\b' | head -2 | sort -r | cut -f2 | xargs echo 15 | )" 16 | 17 | if [[ -z $owner_and_name ]]; then 18 | exit 1 19 | else 20 | echo git://github.com/${owner_and_name/ /\/}.git 21 | fi 22 | -------------------------------------------------------------------------------- /libexec/coral-git-semver-tags: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral git-semver-tags [[@]] 3 | 4 | set -e 5 | [ -n "$CORAL_DEBUG" ] && set -x 6 | 7 | if [ "$1" = '--complete' ]; then 8 | shift 9 | exec coral-path --complete "$@" 10 | fi 11 | 12 | if [ -n "$1" ]; then 13 | dir="$(coral-path "$1")" 14 | export GIT_DIR="${dir}/.git" 15 | fi 16 | 17 | git tag -l | 18 | grep -ve - | # no pre-release tags 19 | grep -E '^v?\d+\.' | # only version tags 20 | coral-sort-versions 21 | -------------------------------------------------------------------------------- /libexec/coral-git-workdir: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral git-workdir [-f | --force] [--mv] 3 | # Summary: Create a new working copy for a local git repo 4 | # Help: Creates a new directory and makes it a working copy of an existing git 5 | # repo by setting up necessary symlinks. 6 | # 7 | # Based on https://github.com/git/git/blob/afa0876/contrib/workdir/git-new-workdir 8 | 9 | set -e 10 | [ -n "$CORAL_DEBUG" ] && set -x 11 | 12 | die() { 13 | echo -e "$@" >&2 14 | exit 1 15 | } 16 | 17 | while [[ $# -gt 0 ]]; do 18 | case "$1" in 19 | --force | -f ) 20 | force=1 21 | shift 1 22 | ;; 23 | --mv ) 24 | move=1 25 | shift 1 26 | ;; 27 | -* ) 28 | die "invalid flag: $1" 29 | ;; 30 | * ) 31 | break 32 | ;; 33 | esac 34 | done 35 | 36 | origin="${1?}" 37 | workdir="${2?}" 38 | 39 | relative_link() { 40 | mkdir -p "$(dirname "$2")" 41 | ln -sfhv "$(relative_path "$(dirname "$2")" "$1")" "$2" 42 | } 43 | 44 | relative_path() { 45 | src="$1" 46 | dest="$2" 47 | rel="" 48 | until [[ ${dest:0:${#src}+1} = "${src}/" ]]; do 49 | src="$(dirname "$src")" 50 | [[ $src = / ]] && src="" 51 | rel="${rel}../" 52 | done 53 | 54 | if [[ -z $src ]]; then 55 | echo $dest 56 | else 57 | echo ${rel}${dest#${src}/} 58 | fi 59 | } 60 | 61 | [[ -z $force && -e $workdir ]] && die "destination ${workdir} already exists" 62 | 63 | git_dir=$(cd "$origin" 2>/dev/null && git rev-parse --git-dir 2>/dev/null) || \ 64 | die "not a git repository: ${origin}" 65 | 66 | if [[ $git_dir = . ]]; then 67 | git_dir="$origin" 68 | else 69 | git_dir="${origin}/${git_dir}" 70 | fi 71 | 72 | git_dir="$(cd "$git_dir" && pwd)" 73 | workdir="$(mkdir -p "$workdir" && cd "$workdir" && pwd)" 74 | 75 | [[ -h "${git_dir}/config" ]] && die "error: ${origin} is a working copy itself" 76 | 77 | for item in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn 78 | do 79 | if [[ -z $move ]]; then 80 | relative_link "${git_dir}/${item}" "${workdir}/.git/${item}" 81 | else 82 | rm -f "${workdir}/.git/${item}" 83 | [[ -e "${git_dir}/${item}" ]] && \ 84 | mv -v "${git_dir}/${item}" "${workdir}/.git/${item}" 85 | fi 86 | done 87 | 88 | for file in HEAD description; do 89 | src="${git_dir}/${file}" 90 | dest="${workdir}/.git/${file}" 91 | [[ -f $src && ! -f $dest ]] && cp -v "$src" "$dest" 92 | done 93 | 94 | exit 0 95 | -------------------------------------------------------------------------------- /libexec/coral-github-markup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Usage: coral github-markup ( | -f ) 3 | # Summary: Render markup to HTML 4 | # Help: Renders markup from Markdown, Textile, RDoc, and other types of files 5 | # the same way it is done on GitHub.com. 6 | # 7 | # If `-f ` is given, read data from STDIN instead of a file. The 8 | # filename is used to specify markup format; e.g. ".md". 9 | 10 | file = ARGV[0] 11 | file = ARGV.shift && ARGV.shift if "-f" == file 12 | 13 | abort "usage: #$0 " unless file 14 | 15 | old_level, $-w = $-w, nil 16 | 17 | begin 18 | require 'github/markup' 19 | require 'html/pipeline' 20 | rescue LoadError 21 | unless defined? Gem 22 | require 'rubygems' 23 | retry 24 | else 25 | $stderr.puts "#$0: " + $!.message 26 | exit 2 27 | end 28 | ensure 29 | $-w = old_level 30 | end 31 | 32 | mdown_pipeline = HTML::Pipeline.new [ 33 | HTML::Pipeline::MarkdownFilter, 34 | HTML::Pipeline::SanitizationFilter, 35 | HTML::Pipeline::ImageMaxWidthFilter, 36 | HTML::Pipeline::AutolinkFilter, 37 | HTML::Pipeline::TableOfContentsFilter, 38 | HTML::Pipeline::SyntaxHighlightFilter, 39 | ], :gfm => false, :detect_syntax => false 40 | 41 | GitHub::Markup.markup('github/markdown', /md|mkdn?|mdown|markdown/) do |content| 42 | result = mdown_pipeline.call content 43 | result[:output] 44 | end 45 | 46 | puts GitHub::Markup.render(file, ARGF.read) 47 | -------------------------------------------------------------------------------- /libexec/coral-help: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | [ -n "$CORAL_DEBUG" ] && set -x 4 | 5 | # content from single commented line marked with a word such as "Usage" or "Summary" 6 | extract_line() { 7 | grep "^# ${1}:" "$2" | head -1 | cut -d ' ' -f3- 8 | } 9 | 10 | # content of multiple consecutive commented lines starting with a word such as "Help" 11 | extract_section() { 12 | sed -En "/^# ${1}: /,/^[^#]/s/^# ?//p" "$2" | sed "1s/${1}: //" 13 | } 14 | 15 | print_summaries() { 16 | for command in $(coral-commands -v); do 17 | print_summary "$command" 18 | done 19 | } 20 | 21 | # print aligned command names with help summary 22 | print_summary() { 23 | if [ ! -h "$1" ]; then 24 | local summary=$(extract_line Summary "$1") 25 | if [ -n "$summary" ]; then 26 | local name=$(basename "$1") 27 | echo "${name#coral-}" | awk '{ printf " %-20s ", $1 }' 28 | echo -n $summary 29 | echo 30 | else 31 | return 1 32 | fi 33 | fi 34 | } 35 | 36 | print_help() { 37 | local usage="$(extract_line Usage "$1")" 38 | local halp="$(extract_section Help "$1")" 39 | [ -z "$halp" ] && halp="$(extract_line Summary "$1")" 40 | 41 | if [ -n "$usage" ]; then 42 | echo Usage: $usage 43 | [ -n "$halp" ] && echo && echo "$halp" 44 | else 45 | echo "Sorry, this command isn't documented yet." >&2 46 | return 1 47 | fi 48 | } 49 | 50 | [ "$1" = "--complete" ] && exec coral-commands 51 | 52 | case "$1" in 53 | "") echo "Usage: coral [] 54 | 55 | Some useful sub commands are: 56 | $(print_summaries) 57 | 58 | See 'coral help ' for information on a specific command." 59 | ;; 60 | *) 61 | command_path="$(command -v "coral-$1" || true)" 62 | if [ -n "$command_path" ]; then 63 | print_help "$(which "$command_path")" 64 | else 65 | echo "coral: no such command \`$1'" >&2 66 | exit 1 67 | fi 68 | esac 69 | -------------------------------------------------------------------------------- /libexec/coral-init: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | 4 | print="" 5 | if [ "$1" = "-" ]; then 6 | print=1 7 | shift 8 | fi 9 | 10 | shell="$(basename "${1:-$SHELL}")" 11 | 12 | if [ -z "$print" ]; then 13 | case "$shell" in 14 | bash ) 15 | profile='~/.bash_profile' 16 | ;; 17 | zsh ) 18 | profile='~/.zshenv' 19 | ;; 20 | * ) 21 | profile='your profile' 22 | ;; 23 | esac 24 | 25 | { echo "# Load Coral automatically by adding" 26 | echo "# the following to ${profile}:" 27 | echo 28 | echo "eval \"\$($(command -v coral) init -)\"" 29 | echo 30 | } >&2 31 | 32 | exit 1 33 | fi 34 | 35 | root="$(dirname "$(dirname "$0")")" 36 | 37 | echo "export PATH=\"${CORAL_ROOT}/bin:\${PATH}\"" 38 | if [ "$root" != "$CORAL_ROOT" ]; then 39 | echo "export PATH=\"\${PATH}:${root}/bin\"" 40 | fi 41 | 42 | case "$shell" in 43 | bash | zsh ) 44 | echo "source \"${root}/completions/coral.${shell}\"" 45 | ;; 46 | esac 47 | 48 | commands=(`coral commands --sh`) 49 | IFS="|" 50 | cat <] 3 | # Summary: List local repositories in Coral 4 | # Help: Lists names of repositories in $CORAL_ROOT, optionally filtered to ones 5 | # starting with . 6 | 7 | set -e 8 | [ -n "$CORAL_DEBUG" ] && set -x 9 | 10 | if [ "$1" = '--complete' ]; then 11 | complete=1 12 | shift 13 | fi 14 | 15 | prefix="$1" 16 | 17 | shopt -s failglob 18 | set -o pipefail 19 | 20 | { for repo_dir in "$CORAL_ROOT/repos/${prefix}"*; do 21 | [[ ! -L ${repo_dir}/.git/config ]] || continue 22 | name=${repo_dir##*/} 23 | if [ -n "$complete" ]; then 24 | echo ${name%@*} 25 | else 26 | versions="$(coral-list-checkouts "$name")" 27 | echo ${name%@*} "(${versions//$'\n'/, })" 28 | fi 29 | done 30 | } | sort --ignore-case 31 | -------------------------------------------------------------------------------- /libexec/coral-list-checkouts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral list-checkouts @ 3 | # Summary: List checkouts of a Coral repo 4 | 5 | set -e 6 | [ -n "$CORAL_DEBUG" ] && set -x 7 | 8 | name="${1?}" 9 | readlink=$(type -p greadlink readlink | head -1) 10 | 11 | echo ${name#*@} 12 | 13 | shopt -s failglob 14 | set -o pipefail 15 | 16 | { for repo_dir in "$CORAL_ROOT/repos/${name%@*}@"*; do 17 | if $readlink "${repo_dir}/.git/config" | grep "/${name}/" >/dev/null; then 18 | version="$(basename "$repo_dir")" 19 | echo ${version#*@} 20 | fi 21 | done 22 | } | coral-sort-versions 23 | -------------------------------------------------------------------------------- /libexec/coral-open-dir: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral open-dir [] 3 | # Summary: Open a project in a directory in the text editor 4 | # Help: Opens a project in the specified directory in $EDITOR. 5 | # 6 | # If the editor is Vim, these special features are supported: 7 | # - automatically cd into the directory inside Vim 8 | # - passing of additional files to open buffers for, such as "README" 9 | # - passing of extra arguments, such as `-t ` 10 | 11 | set -e 12 | [ -n "$CORAL_DEBUG" ] && set -x 13 | 14 | dir="$1" 15 | [[ $# -gt 1 ]] && shift 16 | 17 | EDITOR="${EDITOR:-vi}" 18 | 19 | case ${EDITOR%% *} in 20 | vim | mvim | gvim ) 21 | exec $EDITOR --cmd "cd ${dir// /\\ }" --cmd 'set tags^=.git/tags' "$@" 22 | ;; 23 | subl ) 24 | exec $EDITOR "$@" 25 | ;; 26 | * ) 27 | exec $EDITOR "$dir" 28 | ;; 29 | esac 30 | -------------------------------------------------------------------------------- /libexec/coral-parse-json: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Usage: coral parse-json 3 | # Summary: Output JSON structures in a flat line-based format 4 | 5 | require 'rubygems' 6 | require 'json' 7 | 8 | $\ = "\n" 9 | json = ARGF.read 10 | exit 2 if json.empty? 11 | 12 | def dump obj, parent_key = nil 13 | case obj 14 | when Array 15 | obj.each_with_index {|o,i| dump(o, "#{parent_key}[#{i}]") } 16 | when Hash 17 | prefix = parent_key ? "#{parent_key}." : '' 18 | obj.each {|key, value| dump(value, "#{prefix}#{key}") } 19 | else 20 | if dump_value? obj 21 | $> << parent_key << "\t" 22 | print obj 23 | end 24 | end 25 | end 26 | 27 | def dump_value? value 28 | !( value.nil? or 29 | value.respond_to?(:include?) && value.include?($/) or 30 | value.respond_to?(:empty?) && value.empty? ) 31 | end 32 | 33 | begin 34 | data = JSON.parse json 35 | rescue 36 | $stderr.puts $!.message 37 | exit 1 38 | else 39 | trap(:PIPE) { exit } 40 | dump data 41 | end 42 | -------------------------------------------------------------------------------- /libexec/coral-path: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral path [@] 3 | # Summary: Show filesystem path for a repo 4 | # Help: Displays the full filesystem path for a repository in Coral. 5 | # 6 | # If there are multiple checkouts for a single repo, it displays the path for 7 | # the main checkout by default (see `coral set-canonical`). You can target 8 | # specific versions with . 9 | 10 | set -e 11 | [ -n "$CORAL_DEBUG" ] && set -x 12 | 13 | if [ "$1" = '--complete' ]; then 14 | shift 15 | exec coral-list --complete "$@" 16 | fi 17 | 18 | name="$1" 19 | repo_path="$CORAL_ROOT/repos/${name}" 20 | 21 | shopt -s nullglob 22 | 23 | if [[ $name = ${name%@*} ]]; then 24 | for repo_dir in "${repo_path}@"*; do 25 | if [[ ! -L ${repo_dir}/.git/config ]]; then 26 | echo "$repo_dir" 27 | exit 0 28 | fi 29 | done 30 | elif [[ -d $repo_path ]]; then 31 | echo "$repo_path" 32 | exit 0 33 | fi 34 | 35 | echo "coral-path: could not find \`${name}'" >&2 36 | exit 1 37 | -------------------------------------------------------------------------------- /libexec/coral-render-markup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral render-markup 3 | # Summary: Output styled HTML rendering of markup 4 | # Help: Renders markup in and outputs the HTML together with CSS that 5 | # resembles READMEs on GitHub and support syntax highlighting. 6 | # 7 | # See `coral-github-markup` 8 | 9 | set -e 10 | [ -n "$CORAL_DEBUG" ] && set -x 11 | 12 | html=$(coral-github-markup "$1") 13 | 14 | echo "" 17 | echo "" 18 | 19 | echo "$html" 20 | -------------------------------------------------------------------------------- /libexec/coral-set-canonical: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Usage: coral set-canonical @ 3 | # Summary: Define the main checkout for this repo 4 | # Help: Changes which checkout of this repo is the canonical (i.e. main) one. 5 | # The main checkout is the default pick for commands that operate on a Coral 6 | # repo but don't specify an explicit version. 7 | # 8 | # The first repo that was cloned is always the main checkout, and additional 9 | # checkouts created with `coral checkout` are just links to the main one. This 10 | # allows you to reassign the main checkout. 11 | 12 | set -e 13 | [ -n "$CORAL_DEBUG" ] && set -x 14 | 15 | name="${1%@*}" 16 | version="${1#*@}" 17 | target_dir="$(coral-path "$1")" 18 | 19 | readlink=$(type -p greadlink readlink | head -1) 20 | link="$($readlink "${target_dir}/.git/config")" 21 | old_canonical=${link#*/$name@} 22 | source_dir="$(coral-path "${name}@${old_canonical%%/*}")" 23 | 24 | coral-git-workdir --mv --force "$source_dir" "$target_dir" >/dev/null 25 | 26 | for ver in $(coral-list-checkouts "$(basename "$source_dir")" | grep -vx "$version"); do 27 | dir="$(coral-path "${name}@${ver}")" 28 | coral-git-workdir --force "$target_dir" "$dir" >/dev/null 29 | done 30 | -------------------------------------------------------------------------------- /libexec/coral-sh-cd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | dir="$(command coral path "$@")" 3 | [ -n "$dir" ] && echo cd "\"${dir}\"" 4 | -------------------------------------------------------------------------------- /libexec/coral-sort-versions: -------------------------------------------------------------------------------- 1 | #!/usr/bin/ruby 2 | # Usage: coral sort-versions 3 | # Summary: Sort version numbers 4 | # Help: Sorts version numbers passed on separate lines on STDIN. Results are 5 | # printed from largest number to smallest. 6 | # 7 | # Values that are not numbers are sorted by their alphabetical order. 8 | 9 | require 'strscan' 10 | 11 | # Adapted from Jordi Bunster 12 | # https://github.com/jordi/version_sorter/blob/master/version_sorter.rb 13 | sort = lambda do |list| 14 | ss = StringScanner.new '' 15 | pad_to = 0 16 | list.each { |li| pad_to = li.size if li.size > pad_to } 17 | 18 | list.sort_by do |li| 19 | ss.string = li 20 | parts = '' 21 | 22 | if match = ss.scan_until(/[^.+-]+/) 23 | alpha = match.index(/[^.+\d-]/) 24 | part = match.send(alpha ? :ljust : :rjust, pad_to) 25 | part.sub!(/^(\s+)(\W)/, '\2\1') # put separator to front 26 | part.sub!(/^\+/, '.') # make "+" sort with higher precedence than "-" 27 | parts << part 28 | end until ss.eos? 29 | 30 | # make final versions sort with higher precendence than pre-releases 31 | parts << "." 32 | 33 | parts 34 | end 35 | end 36 | 37 | versions = STDIN.readlines.map {|v| v.chomp } 38 | 39 | if versions.size > 1 40 | sorted = sort.call versions 41 | sorted.reverse! 42 | else 43 | sorted = versions 44 | end 45 | 46 | puts sorted 47 | -------------------------------------------------------------------------------- /resource/readme.css: -------------------------------------------------------------------------------- 1 | body { font: 14px/1.6 helvetica, arial, sans-serif; padding: 30px; max-width: 852px; } 2 | body > *:first-child { margin-top: 0 !important; } 3 | body > *:last-child { margin-bottom: 0 !important; } 4 | 5 | a { color: #4183c4; text-decoration: none; } 6 | 7 | a:hover, a:active { text-decoration: underline; } 8 | 9 | h1, h2, h3, h4, h5, h6 { margin: 20px 0 10px; padding: 0; font-weight: bold; -webkit-font-smoothing: antialiased; } 10 | 11 | h1 { font-size: 28px; color: black; } 12 | 13 | h2 { font-size: 24px; border-bottom: 1px solid #cccccc; color: black; } 14 | 15 | h3 { font-size: 18px; } 16 | 17 | h4 { font-size: 16px; } 18 | 19 | h5 { font-size: 14px; } 20 | 21 | h6 { color: #777777; font-size: 14px; } 22 | 23 | p, blockquote, ul, ol, dl, li, table, pre { margin: 15px 0; } 24 | 25 | hr { background: #eeeeee; border: 0 none; color: #cccccc; height: 4px; padding: 0; } 26 | 27 | body > h2:first-child { border: 0; margin-top: 0; padding-top: 0; } 28 | body > h1:first-child { border: 0; margin-top: 0; padding-top: 0; } 29 | body > h1:first-child + h2 { border: 0; margin-top: 0; padding-top: 0; } 30 | body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child { margin-top: 0; padding-top: 0; } 31 | 32 | h1 + p, h2 + p, h3 + p, h4 + p, h5 + p, h6 + p { margin-top: 0; } 33 | 34 | li p.first { display: inline-block; } 35 | 36 | ul, ol { padding-left: 30px; } 37 | 38 | ul li > :first-child, ol li > :first-child { margin-top: 0; } 39 | 40 | ul li > :last-child, ol li > :last-child { margin-bottom: 0; } 41 | 42 | dl { padding: 0; } 43 | dl dt { font-size: 14px; font-weight: bold; font-style: italic; padding: 0; margin: 15px 0 5px; } 44 | dl dt:first-child { padding: 0; } 45 | dl dt > *:first-child { margin-top: 0; } 46 | dl dt > *:last-child { margin-bottom: 0; } 47 | dl dd { margin: 0 0 15px; padding: 0 15px; } 48 | dl dd > *:first-child { margin-top: 0; } 49 | dl dd > *:last-child { margin-bottom: 0; } 50 | 51 | blockquote { border-left: 4px solid #dddddd; padding: 0 15px; color: #777777; } 52 | 53 | blockquote > *:first-child { margin-top: 0; } 54 | blockquote > *:last-child { margin-bottom: 0; } 55 | 56 | table { border-collapse: collapse; padding: 0; } 57 | table tr { border-top: 1px solid #cccccc; background-color: white; margin: 0; padding: 0; } 58 | table tr:nth-child(2n) { background-color: #f8f8f8; } 59 | table tr th, table tr td { border: 1px solid #cccccc; text-align: left; margin: 0; padding: 6px 13px; } 60 | table tr th > :first-child, table tr td > :first-child { margin-top: 0; } 61 | table tr th > :last-child, table tr td > :last-child { margin-bottom: 0; } 62 | 63 | img { max-width: 100%; } 64 | 65 | code, tt { margin: 0 2px; padding: 0 5px; white-space: nowrap; border: 1px solid #eaeaea; background-color: #f8f8f8; border-radius: 3px; } 66 | 67 | pre > code { margin: 0; padding: 0; white-space: pre; border: none; background: transparent; } 68 | 69 | .highlight pre { background-color: #f8f8f8; border: 1px solid #cccccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; } 70 | 71 | pre { background-color: #f8f8f8; border: 1px solid #cccccc; font-size: 13px; line-height: 19px; overflow: auto; padding: 6px 10px; border-radius: 3px; } 72 | pre > span { padding: 2px 0; } 73 | pre code, pre tt { background-color: transparent; border: none; } 74 | 75 | .highlight { background: white; } 76 | .highlight .c { color: #999988; font-style: italic; } 77 | .highlight .err { color: #a61717; background-color: #e3d2d2; } 78 | .highlight .k, .highlight .o { font-weight: bold; } 79 | .highlight .cm { color: #999988; font-style: italic; } 80 | .highlight .cp { color: #999999; font-weight: bold; } 81 | .highlight .c1 { color: #999988; font-style: italic; } 82 | .highlight .cs { color: #999999; font-weight: bold; font-style: italic; } 83 | .highlight .gd { color: black; background-color: #ffdddd; } 84 | .highlight .gd .x { color: black; background-color: #ffaaaa; } 85 | .highlight .ge { font-style: italic; } 86 | .highlight .gr { color: #aa0000; } 87 | .highlight .gh { color: #999999; } 88 | .highlight .gi { color: black; background-color: #ddffdd; } 89 | .highlight .gi .x { color: black; background-color: #aaffaa; } 90 | .highlight .go { color: #888888; } 91 | .highlight .gp { color: #555555; } 92 | .highlight .gs { font-weight: bold; } 93 | .highlight .gu { color: purple; font-weight: bold; } 94 | .highlight .gt { color: #aa0000; } 95 | .highlight .kc, .highlight .kd, .highlight .kn, .highlight .kp, .highlight .kr { font-weight: bold; } 96 | .highlight .kt { color: #445588; font-weight: bold; } 97 | .highlight .m { color: #009999; } 98 | .highlight .s { color: #dd1144; } 99 | .highlight .na { color: teal; } 100 | .highlight .nb { color: #0086b3; } 101 | .highlight .nc { color: #445588; font-weight: bold; } 102 | .highlight .no { color: teal; } 103 | .highlight .ni { color: purple; } 104 | .highlight .ne, .highlight .nf { color: #990000; font-weight: bold; } 105 | .highlight .nn { color: #555555; } 106 | .highlight .nt { color: navy; } 107 | .highlight .nv { color: teal; } 108 | .highlight .ow { font-weight: bold; } 109 | .highlight .w { color: #bbbbbb; } 110 | .highlight .mf, .highlight .mh, .highlight .mi, .highlight .mo { color: #009999; } 111 | .highlight .sb, .highlight .sc, .highlight .sd, .highlight .s2, .highlight .se, .highlight .sh, .highlight .si, .highlight .sx { color: #dd1144; } 112 | .highlight .sr { color: #009926; } 113 | .highlight .s1 { color: #dd1144; } 114 | .highlight .ss { color: #990073; } 115 | .highlight .bp { color: #999999; } 116 | .highlight .vc, .highlight .vg, .highlight .vi { color: teal; } 117 | .highlight .il { color: #009999; } 118 | 119 | .type-csharp .highlight .k, .type-csharp .highlight .kt { color: blue; } 120 | .type-csharp .highlight .nf { color: black; font-weight: normal; } 121 | .type-csharp .highlight .nc { color: #2b91af; } 122 | .type-csharp .highlight .nn { color: black; } 123 | .type-csharp .highlight .s, .type-csharp .highlight .sc { color: #a31515; } 124 | 125 | .highlight .gc { color: #999999; background-color: #eaf2f5; } 126 | --------------------------------------------------------------------------------