├── .gitignore ├── .travis.yml ├── .version.sh ├── CHANGELOG.md ├── LICENSE ├── README.e.md ├── README.md ├── _config.yml ├── appveyor.yml ├── change.log ├── cli └── cli.go ├── deb.json ├── deprecated └── emd.go ├── emd ├── emd.go └── emd_test.go ├── glide.lock ├── glide.yaml ├── go-nonstd └── emd.go ├── go └── emd.go ├── main.go ├── main_test.go ├── provider ├── provider.go └── std │ ├── provider.go │ └── provider_test.go ├── rpm.json ├── std └── emd.go ├── test.sh ├── utils ├── exec.go ├── md.go ├── md_test.go ├── prelude.go └── shellexec.go └── wix.json /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | vendor/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | 3 | services: 4 | - docker 5 | 6 | language: go 7 | go: 8 | - 1.9 9 | 10 | env: 11 | matrix: 12 | - OKARCH=amd64 OSARCH=amd64 13 | - OKARCH=386 OSARCH=i386 14 | global: 15 | - VERSION=${TRAVIS_TAG} 16 | - GH_USER=${TRAVIS_REPO_SLUG%/*} 17 | - GH_APP=${TRAVIS_REPO_SLUG#*/} 18 | - JFROG_CLI_OFFER_CONFIG=false 19 | # bintray token 20 | - secure: uUsxcTceYD5fMyCm1YaDbOwdDuAUn4MjX861d/EcwpVcuQcWdawewYwg8mAoy+0ast1nJBWZWaJfn3Rj0pBWRaZDmL3A69zs5VJMVOIxlRT4qYvBPaM4nkFBIRaHpY52Ahyzh9Gkfbsl9jWavYvI4LQKKDssygDfsbEu/P9FwYUl3ojCeyvenVjUoXeLZ7bHDPKEizQDfSiHlTerXOHbnv7E0s43awRHsFxPiH81Vv30isyQmkDWwrOQ8CQmIE7wrjooqZQYwbUVejn4h0wywdadN/tvrAjMOynL7cekGtcynqp8tLxY94XPW0jAZaZovwCnu6T2O4JRc9rzEEYMIDBy79P/WYcCTdxo/r61mua8eMOcZxsLdgPnky6dZYFGM5yiDlicJPYJZEAmz6OA+7WnkXfK0CjuX92iDHj5s/5mJivg1n6k2++91MYAmpc+5yIJiDiSumWddZg5UoovCeHK58+DYTDtfaovB7bEUcIWmr4xv5XcXZ8fRZX6CJJPayFcKfEtvNYthOL91/tGGUwjillMtM3OvC0aJb8vZtrXYavD/3Li2TirMLMN0+RDjg3DjCEMvLClfJLtM2aG2+ISFCcn6awsUx8ThwQlrLIKmzXXq+tnUNlcUcb7HhHUQD96WqSKuk38wvM3NS1OGR3ckCo/kfgouQMd+h3BcKE= 21 | 22 | before_install: 23 | - sudo add-apt-repository 'deb https://dl.bintray.com/mh-cbon/deb unstable main' 24 | - sudo apt-get -qq update 25 | - sudo apt-get install --allow-unauthenticated changelog go-bin-deb fakeroot 26 | - mkdir -p ${GOPATH}/bin 27 | - cd ~ 28 | - curl https://glide.sh/get | sh 29 | 30 | install: 31 | - cd $GOPATH/src/github.com/$TRAVIS_REPO_SLUG 32 | - glide install 33 | - go install 34 | 35 | script: 36 | - go test emd/* 37 | - sh test.sh 38 | 39 | before_deploy: 40 | # create the deb package 41 | - cd $GOPATH/src/github.com/$TRAVIS_REPO_SLUG 42 | - mkdir -p build/$OSARCH 43 | - GOOS=linux go build --ldflags "-X main.VERSION=$VERSION" -o build/$OSARCH/$GH_APP main.go 44 | - go-bin-deb generate --file deb.json -a $OSARCH --version $VERSION -o $GH_APP-$OSARCH-$VERSION.deb 45 | # copy the deb for gh release (backward compatibility) 46 | - cp $GH_APP-$OSARCH-$VERSION.deb $GH_APP-$OKARCH.deb 47 | # upload to bintray 48 | - curl -fL https://getcli.jfrog.io | sh 49 | - ls -alh 50 | - ./jfrog bt pc --key=$BTKEY --user=$GH_USER --licenses=MIT --vcs-url=https://github.com/$GH_USER/deb $GH_USER/deb/$GH_APP || echo "package already exists" 51 | - ./jfrog bt upload --override=true --key $BTKEY --publish=true --deb=unstable/main/$OSARCH $GH_APP-$OSARCH-$VERSION.deb $GH_USER/deb/$GH_APP/$VERSION pool/g/$GH_APP/ 52 | # prepare rpm package creation 53 | - docker pull fedora 54 | # create the package in the docker 55 | - > 56 | docker run -v $PWD:/mnt/travis fedora /bin/sh -c 57 | "cd /mnt/travis && (curl -s -L https://bintray.com/mh-cbon/rpm/rpm > /etc/yum.repos.d/w.repo) && dnf install go-bin-rpm changelog rpm-build -y --quiet && go-bin-rpm generate --file rpm.json -a $OSARCH --version $VERSION -o $GH_APP-$OSARCH-$VERSION.rpm" 58 | # copy the rpm for gh release (backward compatibility) 59 | - cp $GH_APP-$OSARCH-$VERSION.rpm $GH_APP-$OKARCH.rpm 60 | # upload to bintray 61 | - ./jfrog bt pc --key=$BTKEY --user=$GH_USER --licenses=MIT --vcs-url=https://github.com/$GH_USER/rpm $GH_USER/rpm/$GH_APP || echo "package already exists" 62 | - ./jfrog bt upload --override=true --key $BTKEY --publish=true $GH_APP-$OSARCH-$VERSION.rpm $GH_USER/rpm/$GH_APP/$VERSION pool/$POOL/$GH_APP/ 63 | # generate the repo metadata 64 | - curl -X POST -u ${GH_USER}:${BTKEY} https://api.bintray.com/calc_metadata/${GH_USER}/rpm 65 | 66 | deploy: 67 | provider: releases 68 | api_key: 69 | secure: em4nCHJdPAXZ+jP/uDflyFfELb9Yn5B3C7rqjBYm1SdTi1KZVZyyOohckoPmljoHt2kva1ls5yszp8pFCj8mFXi1kY6UVV77VC6tYmcrGmP9FXz2iY+X3q2wulu8bU7f+mcG/whfOKPVU/hxLm6iNfcUw3oWNvH8vdZodtBse5UwFi4VkWZBa08Oj9JRR2P0JSMyHsDbvTNeC25xz8j9ytHcv2+j7pvOQf+Swam3z/FknRkG5hxpklMiFCBxKo4FkMKmSNWaH7F2AXGwTyqBgCj4uOEAJsp2Gi27Gw+dC8OOKxx/t9e4l38i5QqWVGVuSFW+wOLwr2StT5rVVG+QDxv61nkh5vLsXckRe7zxA4D2ldf8KtkyLP4vNaAtbUFTmyRJ+I8LhicAn6j4vjOtYpB+CwkGI21if1GEU7fygF7fyIqBss9TOHb4OJUNK16ZVA0do+ZGDcwTgPx11zcgd2Emxl4z2I/ue7OXXJ1hz95tHqMZz8z0hKffdxmVOgp3pQ9w8VoZfBxicGRrWKnr35vH2vkx3R2mmLj7MW3DpDrCRD3qSQ2A7hu4vEiTY0c1yzy6OEWwgfJsx7hnRSU29iaw9Ep0ScXxf/2DtWA6DxY6A/sWd1jGePPlPhOTpx5fdLEiITR0DyHKpe5tHDfXJl6Jn0fwl8nwCc4+bGSAMqk= 70 | file_glob: true 71 | file: 72 | - $GH_APP-$OKARCH.deb 73 | - $GH_APP-$OKARCH.rpm 74 | skip_cleanup: true 75 | on: 76 | tags: true 77 | -------------------------------------------------------------------------------- /.version.sh: -------------------------------------------------------------------------------- 1 | PREBUMP= 2 | 666 git fetch --tags origin master 3 | 666 git pull origin master 4 | 5 | PREVERSION= 6 | philea -s "666 go vet %s" "666 go-fmt-fail %s" 7 | 666 go run main.go -version 8 | 666 go test emd/* -v 9 | 666 go test utils/* -v 10 | 666 go test provider/std/* -v 11 | 666 changelog finalize --version !newversion! 12 | 666 commit -q -m "changelog: !newversion!" -f change.log 13 | 666 changelog md -o CHANGELOG.md --guess 14 | 666 commit -q -m "changelog: !newversion!" -f CHANGELOG.md 15 | 666 go install --ldflags "-X main.VERSION=!newversion!" 16 | 666 emd gen -out README.md 17 | 666 commit -q -m "README: !newversion!" -f README.md 18 | 19 | POSTVERSION= 20 | 666 git push 21 | 666 git push --tags 22 | 666 gh-api-cli create-release -n release -o mh-cbon --guess \ 23 | --ver !newversion! -c "changelog ghrelease --version !newversion!" \ 24 | --draft !isprerelease! 25 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog - emd 2 | 3 | ### 1.0.1 4 | 5 | __Changes__ 6 | 7 | - templates: 8 | - `choco_bintray/install` to show a snippet to install the app from bintray using chocolatey 9 | - `linux/bintray_repo` to show a snippet to install the app from bintray `apt/dnf` repository. 10 | 11 | 12 | 13 | 14 | __Contributors__ 15 | 16 | - mh-cbon 17 | 18 | Released by mh-cbon, Tue 29 Aug 2017 - 19 | [see the diff](https://github.com/mh-cbon/emd/compare/1.0.0...1.0.1#diff) 20 | ______________ 21 | 22 | ### 1.0.0 23 | 24 | __Changes__ 25 | 26 | - toc: fix link generator to remove colon 27 | - close #26: avoid fatal error when a path is not recognized 28 | - appveyor: close #28: fix badge urls 29 | 30 | __Contributors__ 31 | 32 | - mh-cbon 33 | - solvingJ 34 | 35 | Released by mh-cbon, Thu 24 Aug 2017 - 36 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.12...1.0.0#diff) 37 | ______________ 38 | 39 | ### 0.0.12 40 | 41 | __Changes__ 42 | 43 | - __cli__: 44 | - ensure data passed on the command line overwrites everthing else, fix #19. 45 | 46 | - __dep__: 47 | - fixed glide lock, needed an update. 48 | 49 | - __dev__: 50 | - Add provider tests 51 | 52 | 53 | 54 | 55 | __Contributors__ 56 | 57 | - mh-cbon 58 | 59 | Released by mh-cbon, Wed 10 May 2017 - 60 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.11...0.0.12#diff) 61 | ______________ 62 | 63 | ### 0.0.11 64 | 65 | __Changes__ 66 | 67 | - __CLI__: 68 | - Fix an issue in argument handling. 69 | 70 | - __Path detection__: #17 71 | - The mechanism to detect and extract information from the path, 72 | is improved to work with anything that matches src/[provider]/[user]/[repo]/[path...] 73 | 74 | - __Temaplate func helpers__: 75 | - __pkg_doc__: won t panic anymore if the default `main.go` file is not yet created. 76 | 77 | - __Temaplates__: #16 78 | - Fix templates output to avoid double `/`. 79 | 80 | - __dev__: 81 | - Largely improved tests. 82 | 83 | 84 | 85 | 86 | __Contributors__ 87 | 88 | - mh-cbon 89 | - suntong 90 | 91 | Released by mh-cbon, Sun 07 May 2017 - 92 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.10...0.0.11#diff) 93 | ______________ 94 | 95 | ### 0.0.10 96 | 97 | __Changes__ 98 | 99 | - __CLI__: 100 | - new `init` command: Writes a basic `README.e.md` frile to get started 101 | - `gen`: removed useless confirmation message on successful operation. 102 | - `stdin`: emd can now receives the input template from STDIN. 103 | - verbosity: Added support for verbosity with env variable `VERBOSE=y`. 104 | 105 | - __Predefined data__: 106 | - New api is introduced to better detect predefined data. 107 | - ProjectPath is better handled when its a symlink outside of `GOPATH` (#15 thanks suntong) 108 | 109 | - __Prelude data__: 110 | - It is now possible to override predefined data within the prelude. 111 | This should allow the end user to recover from a buggy implementation in the pre defined variables declaration. 112 | 113 | - __TOC__: 114 | - #16 Improve link generator to handle `[]|',` 115 | - Multiple fixes to properly render the TOC level of each header. 116 | 117 | - __Template__: added multiple new functions to help to work with templates 118 | - __set__(name string, x interface{}): Save given value `x` as `name` on dot `.`. 119 | - __link__(url string, text ...string) string: Prints markdown link. 120 | - __img__(url string, alt ...string) string: Prints markdown image. 121 | - __concat__(x ...string) string: Concat given arguments. 122 | - __pathjoin__(x ...string) string: Join given arguments with `/`. 123 | 124 | - __dev__: 125 | - added small test suites into `test.sh` 126 | 127 | 128 | 129 | 130 | __Contributors__ 131 | 132 | - mh-cbon 133 | - suntong 134 | 135 | Released by mh-cbon, Sat 06 May 2017 - 136 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.10-beta...0.0.10#diff) 137 | ______________ 138 | 139 | ### 0.0.10-beta 140 | 141 | __Changes__ 142 | 143 | - CLI: 144 | - new `init` command: Writes a basic `README.e.md` frile to get started 145 | - `gen`: removed useless confirmation message on successful operation. 146 | - Predefined data: 147 | - New api is introduced to better detect predefined data. 148 | - Prelude data: 149 | - It is now possible to override predefined data within the prelude. 150 | This should allow the end user to recover from a buggy implementation in the pre defined variables declaration. 151 | - TOC: 152 | - #16 Improve link generator to handle `[]|',` 153 | - Multiple fixes to properly render the TOC level of each header. 154 | - Template: added multiple new functions to help to work with templates 155 | - __set__(name string, x interface{}): Save given value `x` as `name` on dot `.`. 156 | - __link__(url string, text ...string) string: Prints markdown link. 157 | - __img__(url string, alt ...string) string: Prints markdown image. 158 | - __concat__(x ...string) string: Concat given arguments. 159 | - __pathjoin__(x ...string) string: Join given arguments with `/`. 160 | 161 | 162 | 163 | 164 | __Contributors__ 165 | 166 | - mh-cbon 167 | 168 | Released by mh-cbon, Mon 24 Apr 2017 - 169 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9...0.0.10-beta#diff) 170 | ______________ 171 | 172 | ### 0.0.9 173 | 174 | __Changes__ 175 | 176 | - feature #10: Emd file can define a prelude block of `yaml` data to inject into the template processing. 177 | - cli fix: Before the -in argument was mandatory. It was not possible to use the default template. 178 | - feature installer: Fixed apt/rpm repositories. 179 | - feature #8: ensure an errored command line execution displays correctly. 180 | - feature: added new template functions 181 | - __yaml__(file string, keypaths ...string): parses and build new yaml content of given file. 182 | - __preline__(pre , content string): prepends pre for every lines of content. 183 | - __echo__(s ...string): echo every string s. 184 | - __read__(file string): returns file content. 185 | - __cat__(file string): to display the file content. 186 | - __exec__(bin string, args ...string): to exec a program. 187 | - __shell__(s string): to exec a command line on the underlying shell (it is not cross compatible). 188 | - __color__(color string, content string): to embed content in a block code with color. 189 | - __gotest__(rpkg string, run string, args ...string): exec `go test -v -run `. 190 | - __toc__(maximportance string, title string): display a TOC. 191 | - feature: added new badge templates 192 | - __license/shields__: show a license badge 193 | - __badge/codeship__: show a codeship badge 194 | - __deprecation__ #7: some template functions were deprecated to avoid pre defined formatting, 195 | old behavior can be reset via new options defined into the prelude data. See also the new __color__ function. 196 | - __file__ is dprecated for __cat__ 197 | - __cli__ is dprecated for __exec__ 198 | - badges fix #14: removed useless whitespace 199 | - dev: Added support for glide, it was required to handle yaml. 200 | - dev: updated tests 201 | - dev: godoc documentation improvements. 202 | 203 | __Contributors__ 204 | 205 | - suntong 206 | - mh-cbon 207 | 208 | Released by mh-cbon, Sat 22 Apr 2017 - 209 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta9...0.0.9#diff) 210 | ______________ 211 | 212 | ### 0.0.9-beta9 213 | 214 | __Changes__ 215 | 216 | - fix default reading of the md file 217 | 218 | __Contributors__ 219 | 220 | - mh-cbon 221 | 222 | Released by mh-cbon, Tue 18 Apr 2017 - 223 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta8...0.0.9-beta9#diff) 224 | ______________ 225 | 226 | ### 0.0.9-beta8 227 | 228 | __Changes__ 229 | 230 | - fix #15: properly handle symbolic links 231 | - fix cli: it was not possible to use the default template 232 | 233 | __Contributors__ 234 | 235 | - mh-cbon 236 | 237 | Released by mh-cbon, Tue 18 Apr 2017 - 238 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta7...0.0.9-beta8#diff) 239 | ______________ 240 | 241 | ### 0.0.9-beta7 242 | 243 | __Changes__ 244 | 245 | - fix wrong import path 246 | 247 | __Contributors__ 248 | 249 | - mh-cbon 250 | 251 | Released by mh-cbon, Mon 17 Apr 2017 - 252 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta6...0.0.9-beta7#diff) 253 | ______________ 254 | 255 | ### 0.0.9-beta6 256 | 257 | __Changes__ 258 | 259 | - ci: fix scripts to add glide support 260 | 261 | __Contributors__ 262 | 263 | - mh-cbon 264 | 265 | Released by mh-cbon, Mon 17 Apr 2017 - 266 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta5...0.0.9-beta6#diff) 267 | ______________ 268 | 269 | ### 0.0.9-beta5 270 | 271 | __Changes__ 272 | 273 | - new functions: 274 | - __yaml__(file string, keypaths ...string): parses and build new yaml content of given file. 275 | - __preline__(pre , content string): prepends pre for every lines of content. 276 | - __echo__(s ...string): echo every string s. 277 | - __read__(file string): returns file content. 278 | - toc: multiple fixes, 279 | - it properly handles duplicated title by appending an increment 280 | - fix handling of !; in links generator 281 | - fix line counting when extracting markdown titles 282 | - fix md title selection starting at line N 283 | - prelude: 284 | - fix read of quoted values 285 | - prelude data is now read on all templates added, not only file 286 | - fix last eol handling 287 | - codeship fix #2: proper project url 288 | - bump script: added new utils/ tests 289 | - glide: init versionned dependencies to handle yaml files. 290 | - godoc: refactoring to improve documentation 291 | 292 | __Contributors__ 293 | 294 | - mh-cbon 295 | 296 | Released by mh-cbon, Mon 17 Apr 2017 - 297 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta4...0.0.9-beta5#diff) 298 | ______________ 299 | 300 | ### 0.0.9-beta4 301 | 302 | __Changes__ 303 | 304 | - toc: improve toc parser, refactored, added tests 305 | 306 | __Contributors__ 307 | 308 | - mh-cbon 309 | 310 | Released by mh-cbon, Fri 14 Apr 2017 - 311 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta3...0.0.9-beta4#diff) 312 | ______________ 313 | 314 | ### 0.0.9-beta3 315 | 316 | __Changes__ 317 | 318 | - fix #13: add new template to show a license badge. 319 | - prelude: trim leading whitespaces of unquoted values. 320 | - fix #14: improved badge output, removed useless whitespace. 321 | - fix #2: codeship badge template, added a CsProjectID parameter. 322 | - exec/shell/cat/gotest: avoid pre defined formatting, old behavior can be reset via new options defined into the prelude data. 323 | - toc: fixed some corner cases while parsing/generating the TOC. 324 | 325 | __Contributors__ 326 | 327 | - mh-cbon 328 | 329 | Released by mh-cbon, Fri 14 Apr 2017 - 330 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta2...0.0.9-beta3#diff) 331 | ______________ 332 | 333 | ### 0.0.9-beta2 334 | 335 | __Changes__ 336 | 337 | - fix some bugs in TOC title evaluation and generation 338 | - fix apt repository! 339 | 340 | __Contributors__ 341 | 342 | - mh-cbon 343 | 344 | Released by mh-cbon, Thu 13 Apr 2017 - 345 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta1...0.0.9-beta2#diff) 346 | ______________ 347 | 348 | ### 0.0.9-beta1 349 | 350 | __Changes__ 351 | 352 | - deprecation: improve error messages 353 | 354 | __Contributors__ 355 | 356 | - mh-cbon 357 | 358 | Released by mh-cbon, Wed 12 Apr 2017 - 359 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.9-beta...0.0.9-beta1#diff) 360 | ______________ 361 | 362 | ### 0.0.9-beta 363 | 364 | __Changes__ 365 | 366 | - close #10: added feature to read, decode and registers the prelude data 367 | 368 | It is now possible to define a prelude block of `yaml` data in your __README__ file to 369 | register new data. 370 | 371 | - added __cat/exec/shell/color/gotest/toc__ func 372 | 373 | - __cat__(file string): to display the file content. 374 | - __exec__(bin string, args ...string): to exec a program. 375 | - __shell__(s string): to exec a command line on the underlying shell (it is not cross compatible). 376 | - __color__(color string, content string): to embed content in a block code with color. 377 | - __gotest__(rpkg string, run string, args ...string): exec `go test -v -run `. 378 | - __toc__(maximportance string, title string): display a TOC. 379 | 380 | - close #7: deprecated __file/cli__ func 381 | 382 | Those two functions are deprecated in flavor of their new equivalents, 383 | __cat/exec__. 384 | 385 | The new functions does not returns a triple backquuotes block code. 386 | They returns the response body only. 387 | A new function helper __color__ is a added to create a block code. 388 | 389 | - close #8: improved cli error output 390 | 391 | Before the output error was not displaying 392 | the command line entirely when it was too long. 393 | Now the error is updated to always display the command line with full length. 394 | 395 | - close #9: add new gotest helper func 396 | - close #12: add toc func 397 | - close #10: ensure unquoted strings are read properly 398 | - close #11: add shell func helper. 399 | 400 | __Contributors__ 401 | 402 | - mh-cbon 403 | 404 | Released by mh-cbon, Wed 12 Apr 2017 - 405 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.8...0.0.9-beta#diff) 406 | ______________ 407 | 408 | ### 0.0.8 409 | 410 | __Changes__ 411 | 412 | - fix goreport badge template 413 | 414 | __Contributors__ 415 | 416 | - mh-cbon 417 | 418 | Released by mh-cbon, Sun 12 Mar 2017 - 419 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.7...0.0.8#diff) 420 | ______________ 421 | 422 | ### 0.0.7 423 | 424 | __Changes__ 425 | 426 | - improve template documentation 427 | - goreport: add template (fixes #4) 428 | 429 | __Contributors__ 430 | 431 | - mh-cbon 432 | 433 | Released by mh-cbon, Sun 12 Mar 2017 - 434 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.6...0.0.7#diff) 435 | ______________ 436 | 437 | ### 0.0.6 438 | 439 | __Changes__ 440 | 441 | - template functions (std): add a new render template function to define additional values (fixes #2) 442 | - template function (std): file takes a new argument to define the colorizer (fixes #1) 443 | - emd: add new methods to access template, out and data 444 | - release: fix missing version to the emd build 445 | - README: multiple improvements. 446 | 447 | __Contributors__ 448 | 449 | - mh-cbon 450 | 451 | Released by mh-cbon, Mon 06 Mar 2017 - 452 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.5...0.0.6#diff) 453 | ______________ 454 | 455 | ### 0.0.5 456 | 457 | __Changes__ 458 | 459 | - badges: add codeship 460 | - Funcs cli/file: changed the MD template to add support for html anchors (before they was using bold tag, now they use a title tag) 461 | - command gen: prints success message only if out is not stdout 462 | - README: added a section to show HTML generation, and a recipe to bump the package. 463 | - release: change bump script format 464 | 465 | __Contributors__ 466 | 467 | - mh-cbon 468 | 469 | Released by mh-cbon, Mon 06 Mar 2017 - 470 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.4...0.0.5#diff) 471 | ______________ 472 | 473 | ### 0.0.4 474 | 475 | __Changes__ 476 | 477 | - changelog: typos 478 | - README: add template helpers documentation 479 | 480 | __Contributors__ 481 | 482 | - mh-cbon 483 | 484 | Released by mh-cbon, Wed 22 Feb 2017 - 485 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.3...0.0.4#diff) 486 | ______________ 487 | 488 | ### 0.0.3 489 | 490 | __Changes__ 491 | 492 | - travis(token): update ghtoken 493 | 494 | __Contributors__ 495 | 496 | - mh-cbon 497 | 498 | Released by mh-cbon, Wed 22 Feb 2017 - 499 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.2...0.0.3#diff) 500 | ______________ 501 | 502 | ### 0.0.2 503 | 504 | __Changes__ 505 | 506 | - README: fix appveyor badge 507 | - badge(update): fix url 508 | - README: fix appveyor badge 509 | - badge(fix): fix appveyor badge 510 | - README: add appveyor badge 511 | - badge(update): update text displayed in ci badges 512 | - README(fix): use correct bin path 513 | - bump(fix): emd gen command was wrong 514 | 515 | __Contributors__ 516 | 517 | - mh-cbon 518 | 519 | Released by mh-cbon, Wed 22 Feb 2017 - 520 | [see the diff](https://github.com/mh-cbon/emd/compare/0.0.1...0.0.2#diff) 521 | ______________ 522 | 523 | ### 0.0.1 524 | 525 | __Changes__ 526 | 527 | - project initialization 528 | 529 | __Contributors__ 530 | 531 | - mh-cbon 532 | 533 | Released by mh-cbon, Wed 22 Feb 2017 - 534 | [see the diff](https://github.com/mh-cbon/emd/compare/9b73c280847b824e4e366bcf3276d4eefecde4de...0.0.1#diff) 535 | ______________ 536 | 537 | 538 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 `o-zef 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.e.md: -------------------------------------------------------------------------------- 1 | --- 2 | License: MIT 3 | LicenseFile: LICENSE 4 | LicenseColor: yellow 5 | --- 6 | # {{.Name}} 7 | 8 | {{template "badge/travis" .}} {{template "badge/appveyor" .}} {{template "badge/goreport" .}} {{template "badge/godoc" .}} {{template "license/shields" .}} 9 | 10 | {{pkgdoc}} 11 | 12 | See [emd README file](https://raw.githubusercontent.com/mh-cbon/emd/master/README.e.md) 13 | 14 | # {{toc 5}} 15 | 16 | # Install 17 | 18 | {{template "gh/releases" .}} 19 | 20 | #### glide 21 | {{template "glide/install" .}} 22 | 23 | #### Bintray 24 | {{template "choco_bintray/install" .}} 25 | 26 | #### Chocolatey 27 | {{template "choco/install" .}} 28 | 29 | #### linux rpm/deb repository 30 | {{template "linux/bintray_repo" .}} 31 | 32 | #### linux rpm/deb standalone package 33 | {{template "linux/gh_pkg" .}} 34 | 35 | # Usage 36 | 37 | #### $ {{exec "emd" "-help" | color "sh"}} 38 | 39 | #### $ {{shell "emd gen -help" | color "sh"}} 40 | 41 | #### $ {{shell "emd init -help" | color "sh"}} 42 | 43 | # Cli examples 44 | 45 | ```sh 46 | 47 | # Init a basic emd file to get started. 48 | emd init 49 | 50 | # Reads content of README.e.md, outputs to README.md 51 | emd gen -out README.md 52 | 53 | # same with data injections, 54 | emd gen -out README.md --data='{"CsUUID":"xxxx"}' 55 | 56 | # use verbose mode 57 | VERBOSE=y emd gen 58 | ``` 59 | 60 | # Templates helper 61 | 62 | #### Define data 63 | 64 | Template data can be defined directly into the `README.e.md` file using a `prelude`, 65 | 66 | ```yml 67 | --- 68 | title: "Easygen - Easy to use universal code/text generator" 69 | date: "2016-01-01T22:13:12-05:00" 70 | categories: ["Tech"] 71 | tags: ["go","programming","easygen","CLI"] 72 | --- 73 | ``` 74 | 75 | This `prelude` must be inserted right before the regular `md` content. 76 | 77 | The keys are injected into the template `dot`, the value are `json` decoded. 78 | 79 | Template can access those data using name: `{{echo "{{.categories}}" "{{.tags}}" }}` 80 | 81 | 82 | #### Data 83 | 84 | | Key | Description | 85 | | --- | --- | 86 | | __ProviderURL__ | The vcs provider url (example: github.com). | 87 | | __ProviderName__ | The vcs provider name (example: github). | 88 | | __Name__ | Project name based on the cwd (example: emd). | 89 | | __User__ | User name based on the cwd (example: mh-cbon). | 90 | | __URL__ | Project url as determined by the cwd (example: github.com/mh-cbon/emd). | 91 | | __ProjectURL__ | Project url as determined by the cwd + relative path (example: github.com/mh-cbon/emd/cmd). | 92 | | __Branch__ | Current vcs branch name (defaults to master). | 93 | 94 | #### Functions 95 | 96 | Functions can be invoked like this `{{echo "{{func \"arg1\" \"arg2\"}}" }}` 97 | 98 | Options are keys to define into the `prelude`: 99 | 100 | ```yaml 101 | --- 102 | emd_cat_pre: "### > " 103 | emd_gotest_pre: "### $ " 104 | emd_exec_pre: "### $ " 105 | emd_shell_pre: "### $ " 106 | --- 107 | ``` 108 | 109 | #### Files functions 110 | 111 | | Name | Description | Options | 112 | | --- | --- | -- | 113 | | __cat__(f string) | Displays a file header.
Read and return the file body. | `emd_cat_pre: "### > "`: string to show right before the file path. | 114 | | __read__(f string) | Read and return the file body. | | 115 | | __yaml__(f string, keypaths ...string) | Parse given file as yaml, locate given path into the yaml content, yaml re encode it, return its string. | | 116 | 117 | #### Templates functions 118 | 119 | | Name | Description | Options | 120 | | --- | --- | -- | 121 | | __render__(name string, data interface{}, keyValues ...interface{}) | Render given `template` name, using `data`.
Additionnal data values can be declared using `keyValues ...interface{}` signature, such as
`render("x", data, "key1", "val1", "key2", "val2")`. | | | 122 | | __set__(name string, x interface{}) | Save given value `x` as `name` on dot `.`. | | 123 | 124 | #### Go utils functions 125 | 126 | | Name | Description | Options | 127 | | --- | --- | -- | 128 | | __pkgdoc__(files ...string) | Read the first of `files`, or `main.go`, lookup for its package comment and return it as plain text. | | 129 | | __gotest__(rpkg string, run string, args ...string) | Run `go test -v -run `, return its output.
`rpkg` can be a path to a relative folder like `./emd`. It will resolve to
`github.com/mh-cbon/emd/emd`| `emd_gotest_pre: "### $ "` defines a sring to show right before the `go test` command line. | 130 | 131 | #### Markdown functions 132 | 133 | | Name | Description | Options | 134 | | --- | --- | -- | 135 | | __color__(color string, content string]) string | Embed given content with triple backquote syntax colorizer support. | | 136 | | __toc__(maxImportance int, title ...string) string | Displays a `TOC` of the `README` file being processed.
`maxImportance` defines the titles to select by their numbers of `#`.
`titles` define the title to display, defaults to `TOC`.
Titles displayed before the call to `{{echo "{{toc x}}" }}` are automatically ignored.| | 137 | | __preline__(pre string, content string) string | Prepend every line of `content` with `pre`. | | 138 | | __echo__(f string) string | Prints `f`, usefull to print strings which contains the template tokens. | | 139 | | __link__(url string, text ...string) string | Prints markdown link. | | 140 | | __img__(url string, alt ...string) string | Prints markdown image. | | 141 | | __concat__(x ...string) string | Concat all `x`. | | 142 | | __pathjoin__(x ...string) string | Join all `x` with `/`. | | 143 | 144 | #### Cli functions 145 | 146 | | Name | Description | Options | 147 | | --- | --- | -- | 148 | | __exec__(bin string, args ...string) | Display a command line header.
Execute and return its response. | `emd_exec_pre: "### > "`: string to show right before the command line. | 149 | | __shell__(s string) | Display a command line header.
Execute the command on a shell, and return the its response. | `emd_shell_pre: "### > "`: string to show right before the command line. | 150 | 151 | #### Deprecated function 152 | 153 | | Name | Description | 154 | | --- | --- | 155 | | __file__(f string[, colorizer string]) | Read and display a file enclosed with triples backquotes. If `colorizer` is empty, it defaults to the file extension. | 156 | | __cli__(bin string, args ...string) | Execute and display a command line enclosed with triples backquotes. The highlight defaults to `sh`. | 157 | 158 | #### Templates 159 | 160 | ##### std 161 | 162 | | Name | Description | Params | 163 | | --- | --- | --- | 164 | | __gh/releases__ | Show a text to link the release page. | | 165 | | __badge/travis__ | Show a travis badge. | | 166 | | __badge/appveyor__ | Show an appveyor badge. | | 167 | | __badge/codeship__ | Show a codeship badge. | __CsProjectID__: The codeship project ID (*123465*).
__CsUUID__: the codeship project UUID (*654654-6465-54...*).
Within your `e.md` file use the `render` function, `{{echo "{{render \"badge/codeship\" . \"CsUUID\" \"xx\" \"CsProjectID\" \"yyy\"}}"}}`.
Via cli, add it with `--data '{"CsUUID": "xx", "CsProjectID":"yy"}'`. | 168 | | __choco_bintray/install__ | Show a snippet to install the package with chocolatey from bintray repos. | __BintrayRepo__: the name of the bintray repo (default: `choco`) | 169 | | __choco/install__ | Show a snippet to install the package with chocolatey. | | 170 | | __linux/gh_src_repo__ | Show an sh snippet to install the package via `rpm|deb|apt` repositories hosted on gh-pages. | | 171 | | __linux/bintray_repo__ | Show an sh snippet to install the package via `rpm|deb|apt` repositories hosted on bintray. | | 172 | | __linux/gh_pkg__ | Show an sh snippet to install the package via standalone packages (deb/rpm). | | 173 | | __license/shields__ | Show a license badge. | __License__: The license name like `MIT`, `BSD`.
__LicenseFile__: The path to the license file.
__LicenseColor__: The color of the badge (defaults t o blue). | 174 | 175 | ##### go 176 | 177 | | Name | Description | Params | 178 | | --- | --- | --- | 179 | | __go/install__ | Show an sh snippet to install the package via `go get`. | | 180 | | __badge/godoc__ | Show a godoc badge. | | 181 | | __badge/goreport__ | Show a goreport badge. | | 182 | 183 | ##### go-nonstd 184 | 185 | | Name | Description | Params | 186 | | --- | --- | --- | 187 | | __glide/install__ | Show an sh snippet to install the package via `glide`. | | 188 | 189 | 190 | # API example 191 | 192 | #### > {{cat "main_test.go" | color "go"}} 193 | 194 | # Recipes 195 | 196 | #### Generate HTML content 197 | 198 | To directly generate HTML content out of `emd` output, for example, with `gh-markdown-cli`, 199 | 200 | ```sh 201 | npm install gh-markdown-cli -g 202 | emd gen | mdown 203 | ``` 204 | 205 | #### Release the project 206 | 207 | ```sh 208 | gump patch -d # check 209 | gump patch # bump 210 | ``` 211 | 212 | # History 213 | 214 | [CHANGELOG](CHANGELOG.md) 215 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # emd 2 | 3 | [![travis Status](https://travis-ci.org/mh-cbon/emd.svg?branch=master)](https://travis-ci.org/mh-cbon/emd) [![Appveyor Status](https://ci.appveyor.com/api/projects/status/github/mh-cbon/emd?branch=master&svg=true)](https://ci.appveyor.com/project/mh-cbon/emd) [![Go Report Card](https://goreportcard.com/badge/github.com/mh-cbon/emd)](https://goreportcard.com/report/github.com/mh-cbon/emd) [![GoDoc](https://godoc.org/github.com/mh-cbon/emd?status.svg)](http://godoc.org/github.com/mh-cbon/emd) [![MIT License](http://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) 4 | 5 | Enhanced Markdown template processor. 6 | 7 | 8 | See [emd README file](https://raw.githubusercontent.com/mh-cbon/emd/master/README.e.md) 9 | 10 | # TOC 11 | - [Install](#install) 12 | - [glide](#glide) 13 | - [Bintray](#bintray) 14 | - [Chocolatey](#chocolatey) 15 | - [linux rpm/deb repository](#linux-rpmdeb-repository) 16 | - [linux rpm/deb standalone package](#linux-rpmdeb-standalone-package) 17 | - [Usage](#usage) 18 | - [$ emd -help](#-emd--help) 19 | - [$ emd gen -help](#-emd-gen--help) 20 | - [$ emd init -help](#-emd-init--help) 21 | - [Cli examples](#cli-examples) 22 | - [Templates helper](#templates-helper) 23 | - [Define data](#define-data) 24 | - [Data](#data) 25 | - [Functions](#functions) 26 | - [Files functions](#files-functions) 27 | - [Templates functions](#templates-functions) 28 | - [Go utils functions](#go-utils-functions) 29 | - [Markdown functions](#markdown-functions) 30 | - [Cli functions](#cli-functions) 31 | - [Deprecated function](#deprecated-function) 32 | - [Templates](#templates) 33 | - [API example](#api-example) 34 | - [> main_test.go](#-main_testgo) 35 | - [Recipes](#recipes) 36 | - [Generate HTML content](#generate-html-content) 37 | - [Release the project](#release-the-project) 38 | - [History](#history) 39 | 40 | # Install 41 | 42 | Check the [release page](https://github.com/mh-cbon/emd/releases)! 43 | 44 | #### glide 45 | ```sh 46 | mkdir -p $GOPATH/src/github.com/mh-cbon/emd 47 | cd $GOPATH/src/github.com/mh-cbon/emd 48 | git clone https://github.com/mh-cbon/emd.git . 49 | glide install 50 | go install 51 | ``` 52 | 53 | #### Bintray 54 | ```sh 55 | choco source add -n=mh-cbon -s="https://api.bintray.com/nuget/mh-cbon/choco" 56 | choco install emd 57 | ``` 58 | 59 | #### Chocolatey 60 | ```sh 61 | choco install emd 62 | ``` 63 | 64 | #### linux rpm/deb repository 65 | ```sh 66 | wget -O - https://raw.githubusercontent.com/mh-cbon/latest/master/bintray.sh \ 67 | | GH=mh-cbon/emd sh -xe 68 | # or 69 | curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/bintray.sh \ 70 | | GH=mh-cbon/emd sh -xe 71 | ``` 72 | 73 | #### linux rpm/deb standalone package 74 | ```sh 75 | curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \ 76 | | GH=mh-cbon/emd sh -xe 77 | # or 78 | wget -q -O - --no-check-certificate \ 79 | https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \ 80 | | GH=mh-cbon/emd sh -xe 81 | ``` 82 | 83 | # Usage 84 | 85 | #### $ emd -help 86 | ```sh 87 | emd - 0.0.0 88 | 89 | Usage 90 | -h Show help 91 | -help 92 | Show help 93 | -v Show version 94 | -version 95 | Show version 96 | 97 | Commands 98 | gen Process an emd file. 99 | init Init a basic emd file. 100 | ``` 101 | 102 | #### $ emd gen -help 103 | ```sh 104 | emd - 0.0.0 105 | 106 | Command "gen": Process an emd file. 107 | -data string 108 | JSON map of data 109 | -h Show help 110 | -help 111 | Show help 112 | -in string 113 | Input src file 114 | -out string 115 | Output destination, defaults to stdout (default "-") 116 | ``` 117 | 118 | #### $ emd init -help 119 | ```sh 120 | emd - 0.0.0 121 | 122 | Command "init": Init a basic emd file. 123 | -force 124 | Force write 125 | -h Show help 126 | -help 127 | Show help 128 | -out string 129 | Out file (default "README.e.md") 130 | ``` 131 | 132 | # Cli examples 133 | 134 | ```sh 135 | 136 | # Init a basic emd file to get started. 137 | emd init 138 | 139 | # Reads content of README.e.md, outputs to README.md 140 | emd gen -out README.md 141 | 142 | # same with data injections, 143 | emd gen -out README.md --data='{"CsUUID":"xxxx"}' 144 | 145 | # use verbose mode 146 | VERBOSE=y emd gen 147 | ``` 148 | 149 | # Templates helper 150 | 151 | #### Define data 152 | 153 | Template data can be defined directly into the `README.e.md` file using a `prelude`, 154 | 155 | ```yml 156 | --- 157 | title: "Easygen - Easy to use universal code/text generator" 158 | date: "2016-01-01T22:13:12-05:00" 159 | categories: ["Tech"] 160 | tags: ["go","programming","easygen","CLI"] 161 | --- 162 | ``` 163 | 164 | This `prelude` must be inserted right before the regular `md` content. 165 | 166 | The keys are injected into the template `dot`, the value are `json` decoded. 167 | 168 | Template can access those data using name: `{{.categories}} {{.tags}}` 169 | 170 | 171 | #### Data 172 | 173 | | Key | Description | 174 | | --- | --- | 175 | | __ProviderURL__ | The vcs provider url (example: github.com). | 176 | | __ProviderName__ | The vcs provider name (example: github). | 177 | | __Name__ | Project name based on the cwd (example: emd). | 178 | | __User__ | User name based on the cwd (example: mh-cbon). | 179 | | __URL__ | Project url as determined by the cwd (example: github.com/mh-cbon/emd). | 180 | | __ProjectURL__ | Project url as determined by the cwd + relative path (example: github.com/mh-cbon/emd/cmd). | 181 | | __Branch__ | Current vcs branch name (defaults to master). | 182 | 183 | #### Functions 184 | 185 | Functions can be invoked like this `{{func "arg1" "arg2"}}` 186 | 187 | Options are keys to define into the `prelude`: 188 | 189 | ```yaml 190 | --- 191 | emd_cat_pre: "### > " 192 | emd_gotest_pre: "### $ " 193 | emd_exec_pre: "### $ " 194 | emd_shell_pre: "### $ " 195 | --- 196 | ``` 197 | 198 | #### Files functions 199 | 200 | | Name | Description | Options | 201 | | --- | --- | -- | 202 | | __cat__(f string) | Displays a file header.
Read and return the file body. | `emd_cat_pre: "### > "`: string to show right before the file path. | 203 | | __read__(f string) | Read and return the file body. | | 204 | | __yaml__(f string, keypaths ...string) | Parse given file as yaml, locate given path into the yaml content, yaml re encode it, return its string. | | 205 | 206 | #### Templates functions 207 | 208 | | Name | Description | Options | 209 | | --- | --- | -- | 210 | | __render__(name string, data interface{}, keyValues ...interface{}) | Render given `template` name, using `data`.
Additionnal data values can be declared using `keyValues ...interface{}` signature, such as
`render("x", data, "key1", "val1", "key2", "val2")`. | | | 211 | | __set__(name string, x interface{}) | Save given value `x` as `name` on dot `.`. | | 212 | 213 | #### Go utils functions 214 | 215 | | Name | Description | Options | 216 | | --- | --- | -- | 217 | | __pkgdoc__(files ...string) | Read the first of `files`, or `main.go`, lookup for its package comment and return it as plain text. | | 218 | | __gotest__(rpkg string, run string, args ...string) | Run `go test -v -run `, return its output.
`rpkg` can be a path to a relative folder like `./emd`. It will resolve to
`github.com/mh-cbon/emd/emd`| `emd_gotest_pre: "### $ "` defines a sring to show right before the `go test` command line. | 219 | 220 | #### Markdown functions 221 | 222 | | Name | Description | Options | 223 | | --- | --- | -- | 224 | | __color__(color string, content string]) string | Embed given content with triple backquote syntax colorizer support. | | 225 | | __toc__(maxImportance int, title ...string) string | Displays a `TOC` of the `README` file being processed.
`maxImportance` defines the titles to select by their numbers of `#`.
`titles` define the title to display, defaults to `TOC`.
Titles displayed before the call to `{{toc x}}` are automatically ignored.| | 226 | | __preline__(pre string, content string) string | Prepend every line of `content` with `pre`. | | 227 | | __echo__(f string) string | Prints `f`, usefull to print strings which contains the template tokens. | | 228 | | __link__(url string, text ...string) string | Prints markdown link. | | 229 | | __img__(url string, alt ...string) string | Prints markdown image. | | 230 | | __concat__(x ...string) string | Concat all `x`. | | 231 | | __pathjoin__(x ...string) string | Join all `x` with `/`. | | 232 | 233 | #### Cli functions 234 | 235 | | Name | Description | Options | 236 | | --- | --- | -- | 237 | | __exec__(bin string, args ...string) | Display a command line header.
Execute and return its response. | `emd_exec_pre: "### > "`: string to show right before the command line. | 238 | | __shell__(s string) | Display a command line header.
Execute the command on a shell, and return the its response. | `emd_shell_pre: "### > "`: string to show right before the command line. | 239 | 240 | #### Deprecated function 241 | 242 | | Name | Description | 243 | | --- | --- | 244 | | __file__(f string[, colorizer string]) | Read and display a file enclosed with triples backquotes. If `colorizer` is empty, it defaults to the file extension. | 245 | | __cli__(bin string, args ...string) | Execute and display a command line enclosed with triples backquotes. The highlight defaults to `sh`. | 246 | 247 | #### Templates 248 | 249 | ##### std 250 | 251 | | Name | Description | Params | 252 | | --- | --- | --- | 253 | | __gh/releases__ | Show a text to link the release page. | | 254 | | __badge/travis__ | Show a travis badge. | | 255 | | __badge/appveyor__ | Show an appveyor badge. | | 256 | | __badge/codeship__ | Show a codeship badge. | __CsProjectID__: The codeship project ID (*123465*).
__CsUUID__: the codeship project UUID (*654654-6465-54...*).
Within your `e.md` file use the `render` function, `{{render "badge/codeship" . "CsUUID" "xx" "CsProjectID" "yyy"}}`.
Via cli, add it with `--data '{"CsUUID": "xx", "CsProjectID":"yy"}'`. | 257 | | __choco_bintray/install__ | Show a snippet to install the package with chocolatey from bintray repos. | __BintrayRepo__: the name of the bintray repo (default: `choco`) | 258 | | __choco/install__ | Show a snippet to install the package with chocolatey. | | 259 | | __linux/gh_src_repo__ | Show an sh snippet to install the package via `rpm|deb|apt` repositories hosted on gh-pages. | | 260 | | __linux/bintray_repo__ | Show an sh snippet to install the package via `rpm|deb|apt` repositories hosted on bintray. | | 261 | | __linux/gh_pkg__ | Show an sh snippet to install the package via standalone packages (deb/rpm). | | 262 | | __license/shields__ | Show a license badge. | __License__: The license name like `MIT`, `BSD`.
__LicenseFile__: The path to the license file.
__LicenseColor__: The color of the badge (defaults t o blue). | 263 | 264 | ##### go 265 | 266 | | Name | Description | Params | 267 | | --- | --- | --- | 268 | | __go/install__ | Show an sh snippet to install the package via `go get`. | | 269 | | __badge/godoc__ | Show a godoc badge. | | 270 | | __badge/goreport__ | Show a goreport badge. | | 271 | 272 | ##### go-nonstd 273 | 274 | | Name | Description | Params | 275 | | --- | --- | --- | 276 | | __glide/install__ | Show an sh snippet to install the package via `glide`. | | 277 | 278 | 279 | # API example 280 | 281 | #### > main_test.go 282 | ```go 283 | package main_test 284 | 285 | import ( 286 | "os" 287 | 288 | "github.com/mh-cbon/emd/emd" 289 | "github.com/mh-cbon/emd/std" 290 | ) 291 | 292 | var projectName = "dummy" 293 | 294 | // ExampleGenerate demonstrates the generation 295 | // of the given README.e.md source file 296 | // to os.Stdout. 297 | func Example() { 298 | 299 | // make a new instance of emd.Generator. 300 | gen := emd.NewGenerator() 301 | 302 | // set the main template. 303 | gen.AddTemplate("{{.Name}}") 304 | 305 | // set the data available in templates. 306 | gen.SetDataMap(map[string]interface{}{"Name": projectName}) 307 | 308 | // register a plugin 309 | if err := std.Register(gen); err != nil { 310 | panic(err) 311 | } 312 | 313 | // process the template. 314 | if err := gen.Execute(os.Stdout); err != nil { 315 | panic(err) 316 | } 317 | // Output: dummy 318 | } 319 | ``` 320 | 321 | # Recipes 322 | 323 | #### Generate HTML content 324 | 325 | To directly generate HTML content out of `emd` output, for example, with `gh-markdown-cli`, 326 | 327 | ```sh 328 | npm install gh-markdown-cli -g 329 | emd gen | mdown 330 | ``` 331 | 332 | #### Release the project 333 | 334 | ```sh 335 | gump patch -d # check 336 | gump patch # bump 337 | ``` 338 | 339 | # History 340 | 341 | [CHANGELOG](CHANGELOG.md) 342 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Visual Studio 2017 2 | clone_folder: c:\gopath\src\github.com\%APPVEYOR_REPO_NAME% 3 | skip_non_tags: true 4 | 5 | environment: 6 | GOPATH: c:\gopath 7 | JFROG_CLI_OFFER_CONFIG: false 8 | VCS_URL: https://github.com/%APPVEYOR_REPO_NAME% 9 | BT_KEY: 10 | secure: P9xGXGG38T4AvO8XXXXcbaDG2sSN4t6KH92zssGp0nrbyQAZuxHD9F84s6PM9mOD 11 | # CHOCOKEY: 12 | # secure: HRc9tf57V3c3dVyn8hvMkKeiwK2oyWvOSjNXembIAQctNx+GTGBBaHI3bh+8cIgy 13 | GH_TOKEN: 14 | secure: WVMaMjrLzXN8YNcnFRfcucTYWtvoDeE/4b2TUGQBZDvv7u+ERBQ///z5Q8qYSt0L 15 | 16 | # environment: 17 | # GOPATH: c:\gopath 18 | # GO15VENDOREXPERIMENT: 1 19 | # CHOCOKEY: 20 | # secure: HRc9tf57V3c3dVyn8hvMkKeiwK2oyWvOSjNXembIAQctNx+GTGBBaHI3bh+8cIgy 21 | # GHTOKEN: 22 | # secure: WVMaMjrLzXN8YNcnFRfcucTYWtvoDeE/4b2TUGQBZDvv7u+ERBQ///z5Q8qYSt0L 23 | 24 | install: 25 | - choco source add -n=mh-cbon -s="https://api.bintray.com/nuget/mh-cbon/choco" 26 | - choco install changelog gh-api-cli go-msi -y 27 | - refreshenv 28 | - set GH_APP=%APPVEYOR_PROJECT_NAME% 29 | - set GH_USER=%APPVEYOR_ACCOUNT_NAME% 30 | - set VERSION=%APPVEYOR_REPO_TAG_NAME% 31 | - if "%x%"=="%VERSION%" set VERSION=1.0.2 32 | - set PATH=%WIX%\bin;%PATH% 33 | - set PATH=%GOPATH%\bin;%PATH% 34 | - curl -fsSk -o jfrog.exe -L "https://api.bintray.com/content/jfrog/jfrog-cli-go/$latest/jfrog-cli-windows-amd64/jfrog.exe?bt_package=jfrog-cli-windows-amd64" 35 | - go get -u github.com/mh-cbon/never-fail 36 | - go get -u github.com/Masterminds/glide 37 | - glide install 38 | 39 | # install: 40 | # - ps: if (-not (Test-Path env:APPVEYOR_REPO_TAG_NAME)) { $env:APPVEYOR_REPO_TAG_NAME = '0.0.2' } 41 | # - curl -fsSL -o C:\wix310-binaries.zip http://static.wixtoolset.org/releases/v3.10.3.3007/wix310-binaries.zip 42 | # - 7z x C:\wix310-binaries.zip -y -r -oC:\wix310 43 | # - set PATH=C:\wix310;%PATH% 44 | # - set PATH=%GOPATH%\bin;c:\go\bin;%PATH% 45 | # - go version 46 | # - go env 47 | # - curl -fsSL -o C:\latest.bat https://raw.githubusercontent.com/mh-cbon/latest/master/latest.bat?a=1 48 | # - cmd /C C:\latest.bat mh-cbon go-msi amd64 49 | # - set PATH=C:\Program Files\go-msi\;%PATH% 50 | # - go get -u github.com/Masterminds/glide 51 | # - glide install 52 | # - go test github.com/mh-cbon/emd -v 53 | 54 | # build msi artifacts 55 | build_script: 56 | - go test github.com/mh-cbon/emd -v 57 | # x386 58 | - set GOARCH=386 59 | - go build -o %GH_APP%.exe --ldflags "-X main.VERSION=%VERSION%" main.go 60 | - go-msi make --msi %GH_APP%-%GOARCH%-%VERSION%.msi --version %VERSION% --arch %GOARCH% 61 | - cp %GH_APP%-%GOARCH%-%VERSION%.msi %GH_APP%-%GOARCH%.msi 62 | # nuget package is built only for the x86 arch. 63 | - go-msi choco --path wix.json --version %VERSION% --input %GH_APP%-%GOARCH%-%VERSION%.msi --changelog-cmd "changelog ghrelease --version %VERSION%" 64 | # amd64 65 | - set GOARCH=amd64 66 | - go build -o %GH_APP%.exe --ldflags "-X main.VERSION=%VERSION%" main.go 67 | - go-msi make --msi %GH_APP%-%GOARCH%-%VERSION%.msi --version %VERSION% --arch %GOARCH% 68 | - cp %GH_APP%-%GOARCH%-%VERSION%.msi %GH_APP%-%GOARCH%.msi 69 | 70 | # build_script: 71 | # - set GOARCH=386 72 | # - go build -o %APPVEYOR_PROJECT_NAME%.exe --ldflags "-X main.VERSION=%APPVEYOR_REPO_TAG_NAME%" main.go 73 | # - go-msi.exe make --msi %APPVEYOR_BUILD_FOLDER%\%APPVEYOR_PROJECT_NAME%-%GOARCH%.msi --version %APPVEYOR_REPO_TAG_NAME% --arch %GOARCH% 74 | # - set GOARCH=amd64 75 | # - go build -o %APPVEYOR_PROJECT_NAME%.exe --ldflags "-X main.VERSION=%APPVEYOR_REPO_TAG_NAME%" main.go 76 | # - go-msi.exe make --msi %APPVEYOR_BUILD_FOLDER%\%APPVEYOR_PROJECT_NAME%-%GOARCH%.msi --version %APPVEYOR_REPO_TAG_NAME% --arch %GOARCH% 77 | 78 | # after_deploy: 79 | # - go-msi.exe choco --input %APPVEYOR_BUILD_FOLDER%\%APPVEYOR_PROJECT_NAME%-%GOARCH%.msi --version %APPVEYOR_REPO_TAG_NAME% 80 | # - choco push -k="'%CHOCOKEY%'" %APPVEYOR_PROJECT_NAME%.%APPVEYOR_REPO_TAG_NAME%.nupkg 81 | 82 | 83 | deploy_script: 84 | - never-fail jfrog bt pc --user %GH_USER% --key %BT_KEY% --licenses=MIT --vcs-url=https://github.com/%APPVEYOR_REPO_NAME% %GH_USER%/msi/%GH_APP% 85 | - never-fail jfrog bt pc --user %GH_USER% --key %BT_KEY% --licenses=MIT --vcs-url=https://github.com/%APPVEYOR_REPO_NAME% %GH_USER%/choco/%GH_APP% 86 | - never-fail curl -X "DELETE" -u%GH_USER%:%BT_KEY% https://api.bintray.com/content/%GH_USER%/choco/%GH_APP%.%VERSION%.nupkg # workaround until --override=true is honored in next upload 87 | - jfrog bt upload --user %GH_USER% --key %BT_KEY% --override=true --publish=true %GH_APP%.%VERSION%.nupkg %GH_USER%/choco/%GH_APP%/%VERSION% 88 | - set GOARCH=386 89 | - jfrog bt upload --user %GH_USER% --key %BT_KEY% --override=true --publish=true %GH_APP%-%GOARCH%-%VERSION%.msi %GH_USER%/msi/%GH_APP%/%VERSION% 90 | - set GOARCH=amd64 91 | - jfrog bt upload --user %GH_USER% --key %BT_KEY% --override=true --publish=true %GH_APP%-%GOARCH%-%VERSION%.msi %GH_USER%/msi/%GH_APP%/%VERSION% 92 | # next section is a workaround for https://github.com/appveyor/ci/issues/1752 93 | - gh-api-cli upload-release-asset -t %GH_TOKEN% -g "*-386.msi" -o %GH_USER% -r %GH_APP% --ver %VERSION% 94 | - gh-api-cli upload-release-asset -t %GH_TOKEN% -g "*-amd64.msi" -o %GH_USER% -r %GH_APP% --ver %VERSION% 95 | 96 | test: off 97 | 98 | artifacts: 99 | - path: '*-386.msi' 100 | name: msi-x86 101 | - path: '*-amd64.msi' 102 | name: msi-x64 103 | 104 | deploy: 105 | - provider: GitHub 106 | artifact: msi-x86, msi-x64 107 | draft: false 108 | prerelease: false 109 | description: "Release %APPVEYOR_REPO_TAG_NAME%" 110 | auth_token: 111 | secure: WVMaMjrLzXN8YNcnFRfcucTYWtvoDeE/4b2TUGQBZDvv7u+ERBQ///z5Q8qYSt0L 112 | on: 113 | appveyor_repo_tag: true 114 | -------------------------------------------------------------------------------- /change.log: -------------------------------------------------------------------------------- 1 | 2 | 1.0.1 3 | 4 | * templates: 5 | - `choco_bintray/install` to show a snippet to install the app from bintray using chocolatey 6 | - `linux/bintray_repo` to show a snippet to install the app from bintray `apt/dnf` repository. 7 | 8 | 9 | 10 | - mh-cbon 11 | 12 | -- mh-cbon ; Tue, 29 Aug 2017 19:51:46 +0200 13 | 14 | 15 | 16 | 1.0.0 17 | 18 | * toc: fix link generator to remove colon 19 | * close #26: avoid fatal error when a path is not recognized 20 | * appveyor: close #28: fix badge urls 21 | 22 | - mh-cbon 23 | - solvingJ 24 | 25 | -- mh-cbon ; Thu, 24 Aug 2017 18:46:21 +0200 26 | 27 | 28 | 29 | 0.0.12 30 | 31 | * __cli__: 32 | - ensure data passed on the command line overwrites everthing else, fix #19. 33 | 34 | * __dep__: 35 | - fixed glide lock, needed an update. 36 | 37 | * __dev__: 38 | - Add provider tests 39 | 40 | 41 | 42 | - mh-cbon 43 | 44 | -- mh-cbon ; Wed, 10 May 2017 11:30:14 +0200 45 | 46 | 47 | 48 | 0.0.11 49 | 50 | * __CLI__: 51 | - Fix an issue in argument handling. 52 | 53 | * __Path detection__: #17 54 | - The mechanism to detect and extract information from the path, 55 | is improved to work with anything that matches src/[provider]/[user]/[repo]/[path...] 56 | 57 | * __Temaplate func helpers__: 58 | - __pkg_doc__: won t panic anymore if the default `main.go` file is not yet created. 59 | 60 | * __Temaplates__: #16 61 | - Fix templates output to avoid double `/`. 62 | 63 | * __dev__: 64 | - Largely improved tests. 65 | 66 | 67 | 68 | - mh-cbon 69 | - suntong 70 | 71 | -- mh-cbon ; Sun, 07 May 2017 16:05:48 +0200 72 | 73 | 74 | 75 | 0.0.10 76 | 77 | * __CLI__: 78 | - new `init` command: Writes a basic `README.e.md` frile to get started 79 | - `gen`: removed useless confirmation message on successful operation. 80 | - `stdin`: emd can now receives the input template from STDIN. 81 | - verbosity: Added support for verbosity with env variable `VERBOSE=y`. 82 | 83 | * __Predefined data__: 84 | - New api is introduced to better detect predefined data. 85 | - ProjectPath is better handled when its a symlink outside of `GOPATH` (#15 thanks suntong) 86 | 87 | * __Prelude data__: 88 | - It is now possible to override predefined data within the prelude. 89 | This should allow the end user to recover from a buggy implementation in the pre defined variables declaration. 90 | 91 | * __TOC__: 92 | - #16 Improve link generator to handle `[]|',` 93 | - Multiple fixes to properly render the TOC level of each header. 94 | 95 | * __Template__: added multiple new functions to help to work with templates 96 | - __set__(name string, x interface{}): Save given value `x` as `name` on dot `.`. 97 | - __link__(url string, text ...string) string: Prints markdown link. 98 | - __img__(url string, alt ...string) string: Prints markdown image. 99 | - __concat__(x ...string) string: Concat given arguments. 100 | - __pathjoin__(x ...string) string: Join given arguments with `/`. 101 | 102 | * __dev__: 103 | - added small test suites into `test.sh` 104 | 105 | 106 | 107 | - mh-cbon 108 | - suntong 109 | 110 | -- mh-cbon ; Sat, 06 May 2017 09:09:10 +0200 111 | 112 | 113 | 114 | 0.0.10-beta 115 | 116 | * CLI: 117 | - new `init` command: Writes a basic `README.e.md` frile to get started 118 | - `gen`: removed useless confirmation message on successful operation. 119 | * Predefined data: 120 | - New api is introduced to better detect predefined data. 121 | * Prelude data: 122 | - It is now possible to override predefined data within the prelude. 123 | This should allow the end user to recover from a buggy implementation in the pre defined variables declaration. 124 | * TOC: 125 | - #16 Improve link generator to handle `[]|',` 126 | - Multiple fixes to properly render the TOC level of each header. 127 | * Template: added multiple new functions to help to work with templates 128 | - __set__(name string, x interface{}): Save given value `x` as `name` on dot `.`. 129 | - __link__(url string, text ...string) string: Prints markdown link. 130 | - __img__(url string, alt ...string) string: Prints markdown image. 131 | - __concat__(x ...string) string: Concat given arguments. 132 | - __pathjoin__(x ...string) string: Join given arguments with `/`. 133 | 134 | 135 | 136 | - mh-cbon 137 | 138 | -- mh-cbon ; Mon, 24 Apr 2017 14:54:07 +0200 139 | 140 | 141 | 142 | 0.0.9 143 | 144 | * feature #10: Emd file can define a prelude block of `yaml` data to inject into the template processing. 145 | * cli fix: Before the -in argument was mandatory. It was not possible to use the default template. 146 | * feature installer: Fixed apt/rpm repositories. 147 | * feature #8: ensure an errored command line execution displays correctly. 148 | * feature: added new template functions 149 | - __yaml__(file string, keypaths ...string): parses and build new yaml content of given file. 150 | - __preline__(pre , content string): prepends pre for every lines of content. 151 | - __echo__(s ...string): echo every string s. 152 | - __read__(file string): returns file content. 153 | - __cat__(file string): to display the file content. 154 | - __exec__(bin string, args ...string): to exec a program. 155 | - __shell__(s string): to exec a command line on the underlying shell (it is not cross compatible). 156 | - __color__(color string, content string): to embed content in a block code with color. 157 | - __gotest__(rpkg string, run string, args ...string): exec `go test -v -run `. 158 | - __toc__(maximportance string, title string): display a TOC. 159 | * feature: added new badge templates 160 | - __license/shields__: show a license badge 161 | - __badge/codeship__: show a codeship badge 162 | * __deprecation__ #7: some template functions were deprecated to avoid pre defined formatting, 163 | old behavior can be reset via new options defined into the prelude data. See also the new __color__ function. 164 | - __file__ is dprecated for __cat__ 165 | - __cli__ is dprecated for __exec__ 166 | * badges fix #14: removed useless whitespace 167 | * dev: Added support for glide, it was required to handle yaml. 168 | * dev: updated tests 169 | * dev: godoc documentation improvements. 170 | 171 | - suntong 172 | - mh-cbon 173 | 174 | -- mh-cbon ; Sat, 22 Apr 2017 10:51:03 +0200 175 | 176 | 177 | 178 | 0.0.9-beta9 179 | 180 | * fix default reading of the md file 181 | 182 | - mh-cbon 183 | 184 | -- mh-cbon ; Tue, 18 Apr 2017 10:51:03 +0200 185 | 186 | 187 | 188 | 0.0.9-beta8 189 | 190 | * fix #15: properly handle symbolic links 191 | * fix cli: it was not possible to use the default template 192 | 193 | - mh-cbon 194 | 195 | -- mh-cbon ; Tue, 18 Apr 2017 10:46:56 +0200 196 | 197 | 198 | 199 | 0.0.9-beta7 200 | 201 | * fix wrong import path 202 | 203 | - mh-cbon 204 | 205 | -- mh-cbon ; Mon, 17 Apr 2017 16:35:09 +0200 206 | 207 | 208 | 209 | 0.0.9-beta6 210 | 211 | * ci: fix scripts to add glide support 212 | 213 | - mh-cbon 214 | 215 | -- mh-cbon ; Mon, 17 Apr 2017 16:26:18 +0200 216 | 217 | 218 | 219 | 0.0.9-beta5 220 | 221 | * new functions: 222 | - __yaml__(file string, keypaths ...string): parses and build new yaml content of given file. 223 | - __preline__(pre , content string): prepends pre for every lines of content. 224 | - __echo__(s ...string): echo every string s. 225 | - __read__(file string): returns file content. 226 | * toc: multiple fixes, 227 | - it properly handles duplicated title by appending an increment 228 | - fix handling of !; in links generator 229 | - fix line counting when extracting markdown titles 230 | - fix md title selection starting at line N 231 | * prelude: 232 | - fix read of quoted values 233 | - prelude data is now read on all templates added, not only file 234 | - fix last eol handling 235 | * codeship fix #2: proper project url 236 | * bump script: added new utils/ tests 237 | * glide: init versionned dependencies to handle yaml files. 238 | * godoc: refactoring to improve documentation 239 | 240 | - mh-cbon 241 | 242 | -- mh-cbon ; Mon, 17 Apr 2017 16:09:41 +0200 243 | 244 | 245 | 246 | 0.0.9-beta4 247 | 248 | * toc: improve toc parser, refactored, added tests 249 | 250 | - mh-cbon 251 | 252 | -- mh-cbon ; Fri, 14 Apr 2017 15:15:52 +0200 253 | 254 | 255 | 256 | 0.0.9-beta3 257 | 258 | * fix #13: add new template to show a license badge. 259 | * prelude: trim leading whitespaces of unquoted values. 260 | * fix #14: improved badge output, removed useless whitespace. 261 | * fix #2: codeship badge template, added a CsProjectID parameter. 262 | * exec/shell/cat/gotest: avoid pre defined formatting, old behavior can be reset via new options defined into the prelude data. 263 | * toc: fixed some corner cases while parsing/generating the TOC. 264 | 265 | - mh-cbon 266 | 267 | -- mh-cbon ; Fri, 14 Apr 2017 11:58:55 +0200 268 | 269 | 270 | 271 | 0.0.9-beta2 272 | 273 | * fix some bugs in TOC title evaluation and generation 274 | * fix apt repository! 275 | 276 | - mh-cbon 277 | 278 | -- mh-cbon ; Thu, 13 Apr 2017 15:09:44 +0200 279 | 280 | 281 | 282 | 0.0.9-beta1 283 | 284 | * deprecation: improve error messages 285 | 286 | - mh-cbon 287 | 288 | -- mh-cbon ; Wed, 12 Apr 2017 17:15:39 +0200 289 | 290 | 291 | 292 | 0.0.9-beta 293 | 294 | * close #10: added feature to read, decode and registers the prelude data 295 | 296 | It is now possible to define a prelude block of `yaml` data in your __README__ file to 297 | register new data. 298 | 299 | * added __cat/exec/shell/color/gotest/toc__ func 300 | 301 | - __cat__(file string): to display the file content. 302 | - __exec__(bin string, args ...string): to exec a program. 303 | - __shell__(s string): to exec a command line on the underlying shell (it is not cross compatible). 304 | - __color__(color string, content string): to embed content in a block code with color. 305 | - __gotest__(rpkg string, run string, args ...string): exec `go test -v -run `. 306 | - __toc__(maximportance string, title string): display a TOC. 307 | 308 | * close #7: deprecated __file/cli__ func 309 | 310 | Those two functions are deprecated in flavor of their new equivalents, 311 | __cat/exec__. 312 | 313 | The new functions does not returns a triple backquuotes block code. 314 | They returns the response body only. 315 | A new function helper __color__ is a added to create a block code. 316 | 317 | * close #8: improved cli error output 318 | 319 | Before the output error was not displaying 320 | the command line entirely when it was too long. 321 | Now the error is updated to always display the command line with full length. 322 | 323 | * close #9: add new gotest helper func 324 | * close #12: add toc func 325 | * close #10: ensure unquoted strings are read properly 326 | * close #11: add shell func helper. 327 | 328 | - mh-cbon 329 | 330 | -- mh-cbon ; Wed, 12 Apr 2017 14:36:51 +0200 331 | 332 | 333 | 334 | 0.0.8 335 | 336 | * fix goreport badge template 337 | 338 | - mh-cbon 339 | 340 | -- mh-cbon ; Sun, 12 Mar 2017 01:28:04 +0100 341 | 342 | 343 | 344 | 0.0.7 345 | 346 | * improve template documentation 347 | * goreport: add template (fixes #4) 348 | 349 | - mh-cbon 350 | 351 | -- mh-cbon ; Sun, 12 Mar 2017 01:23:58 +0100 352 | 353 | 354 | 355 | 0.0.6 356 | 357 | * template functions (std): add a new render template function to define additional values (fixes #2) 358 | * template function (std): file takes a new argument to define the colorizer (fixes #1) 359 | * emd: add new methods to access template, out and data 360 | * release: fix missing version to the emd build 361 | * README: multiple improvements. 362 | 363 | - mh-cbon 364 | 365 | -- mh-cbon ; Mon, 06 Mar 2017 19:18:04 +0100 366 | 367 | 368 | 369 | 0.0.5 370 | 371 | * badges: add codeship 372 | * Funcs cli/file: changed the MD template to add support for html anchors (before they was using bold tag, now they use a title tag) 373 | * command gen: prints success message only if out is not stdout 374 | * README: added a section to show HTML generation, and a recipe to bump the package. 375 | * release: change bump script format 376 | 377 | - mh-cbon 378 | 379 | -- mh-cbon ; Mon, 06 Mar 2017 15:20:40 +0100 380 | 381 | 382 | 383 | 0.0.4 384 | 385 | * changelog: typos 386 | * README: add template helpers documentation 387 | 388 | - mh-cbon 389 | 390 | -- mh-cbon ; Wed, 22 Feb 2017 22:18:19 +0100 391 | 392 | 393 | 394 | 0.0.3 395 | 396 | * travis(token): update ghtoken 397 | 398 | - mh-cbon 399 | 400 | -- mh-cbon ; Wed, 22 Feb 2017 22:05:07 +0100 401 | 402 | 403 | 404 | 0.0.2 405 | 406 | * README: fix appveyor badge 407 | * badge(update): fix url 408 | * README: fix appveyor badge 409 | * badge(fix): fix appveyor badge 410 | * README: add appveyor badge 411 | * badge(update): update text displayed in ci badges 412 | * README(fix): use correct bin path 413 | * bump(fix): emd gen command was wrong 414 | 415 | - mh-cbon 416 | 417 | -- mh-cbon ; Wed, 22 Feb 2017 21:26:29 +0100 418 | 419 | 420 | 421 | 0.0.1 422 | 423 | * project initialization 424 | 425 | - mh-cbon 426 | 427 | -- mh-cbon ; Wed, 22 Feb 2017 21:07:17 +0100 428 | 429 | 430 | -------------------------------------------------------------------------------- /cli/cli.go: -------------------------------------------------------------------------------- 1 | // Package cli handles command line arguments. 2 | package cli 3 | 4 | import ( 5 | "flag" 6 | "fmt" 7 | "os" 8 | ) 9 | 10 | // NewProgram makes a new Program instance. 11 | func NewProgram(name string, version string) *Program { 12 | return &Program{map[string]Commander{}, name, version, false, false, false, false} 13 | } 14 | 15 | // NewCommand makes a new Program instance. 16 | func NewCommand(name string, desc string, fn func(s Commander) error) *Command { 17 | set := flag.NewFlagSet(name, flag.ExitOnError) 18 | set.SetOutput(os.Stdout) 19 | return &Command{name, desc, set, fn} 20 | } 21 | 22 | // Program is a struct to define a program and its command. 23 | type Program struct { 24 | commands map[string]Commander 25 | ProgramName string 26 | ProgramVersion string 27 | Help bool 28 | ShortHelp bool 29 | Version bool 30 | ShortVersion bool 31 | } 32 | 33 | // Bind help and version flag. 34 | func (p *Program) Bind() { 35 | flag.CommandLine.Init(p.ProgramName, flag.ExitOnError) 36 | flag.CommandLine.SetOutput(os.Stdout) 37 | flag.BoolVar(&p.ShortHelp, "h", false, "Show help") 38 | flag.BoolVar(&p.Help, "help", false, "Show help") 39 | flag.BoolVar(&p.Version, "version", false, "Show version") 40 | flag.BoolVar(&p.ShortVersion, "v", false, "Show version") 41 | } 42 | 43 | // Add a new sub command. 44 | func (p *Program) Add(c Commander) bool { 45 | p.commands[c.getName()] = c 46 | return true 47 | } 48 | 49 | // ShowVersion prints program name and version on stderr. 50 | func (p *Program) ShowVersion() error { 51 | fmt.Fprintf(os.Stdout, "%s - %v\n", p.ProgramName, p.ProgramVersion) 52 | return nil 53 | } 54 | 55 | // ShowUsage prints program usage of given subCmd on stderr. If subCmd is empty, prints general usage. 56 | func (p *Program) ShowUsage(subCmd string) error { 57 | if subCmd == "" { 58 | p.ShowVersion() 59 | fmt.Fprintln(os.Stdout, "\nUsage") 60 | flag.PrintDefaults() 61 | fmt.Fprintln(os.Stdout, "\nCommands") 62 | for name, c := range p.commands { 63 | fmt.Fprintf(os.Stderr, "\t%v\t%v\n", name, c.getDesc()) 64 | } 65 | return nil 66 | } 67 | if cmd := p.commands[subCmd]; cmd != nil { 68 | return p.ShowCmdUsage(cmd) 69 | } 70 | return fmt.Errorf("No such command %q", subCmd) 71 | } 72 | 73 | // ShowCmdUsage prints command usage of given subCmd on stderr. 74 | func (p *Program) ShowCmdUsage(cmd Commander) error { 75 | p.ShowVersion() 76 | fmt.Fprintf(os.Stdout, "\nCommand %q: %v\n", cmd.getName(), cmd.getDesc()) 77 | cmd.getSet().PrintDefaults() 78 | return nil 79 | } 80 | 81 | // Run the program against given set of arguments. 82 | func (p *Program) Run(args []string) error { 83 | if len(args) > 1 { 84 | 85 | if cmd, ok := p.commands[args[1]]; ok { 86 | if err := cmd.getSet().Parse(args[2:]); err != nil { 87 | return nil 88 | } 89 | } else { 90 | flag.Parse() 91 | } 92 | 93 | for name, cmd := range p.commands { 94 | if cmd.getSet().Parsed() { 95 | err := cmd.getFn()(cmd) 96 | if err != nil { 97 | err = fmt.Errorf("command %q failed: %v", name, err) 98 | } 99 | return err 100 | } 101 | } 102 | } 103 | 104 | if p.Version || p.ShortVersion { 105 | return p.ShowVersion() 106 | } 107 | 108 | return p.ShowUsage("") 109 | } 110 | 111 | // Commander is an generalizer of Command. 112 | type Commander interface { 113 | getDesc() string 114 | getName() string 115 | getSet() *flag.FlagSet 116 | getFn() func(s Commander) error 117 | } 118 | 119 | // Command describe a program sub command. 120 | type Command struct { 121 | name string 122 | desc string 123 | Set *flag.FlagSet 124 | fn func(s Commander) error 125 | } 126 | 127 | func (c *Command) getDesc() string { 128 | return c.desc 129 | } 130 | func (c *Command) getName() string { 131 | return c.name 132 | } 133 | func (c *Command) getSet() *flag.FlagSet { 134 | return c.Set 135 | } 136 | func (c *Command) getFn() func(s Commander) error { 137 | return c.fn 138 | } 139 | -------------------------------------------------------------------------------- /deb.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emd", 3 | "maintainer": "mh-cbon ", 4 | "description": "Enhanced Markdown template processor", 5 | "changelog-cmd": "changelog debian --vars='{\"name\":\"!name!\"}'", 6 | "homepage": "http://github.com/mh-cbon/!name!", 7 | "files": [ 8 | { 9 | "from": "build/!arch!/!name!", 10 | "to": "/usr/bin", 11 | "base" : "build/!arch!/", 12 | "fperm": "0755" 13 | } 14 | ], 15 | "copyrights": [ 16 | { 17 | "files": "*", 18 | "copyright": "2016 mh-cbon ", 19 | "license": "MIT", 20 | "file": "LICENSE" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /deprecated/emd.go: -------------------------------------------------------------------------------- 1 | // Package deprecated contains deprecated helpers. 2 | package deprecated 3 | 4 | import ( 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "os/exec" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/mh-cbon/emd/emd" 13 | ) 14 | 15 | // CliError is an error of cli command 16 | type CliError struct { 17 | Err error 18 | Cmd string 19 | } 20 | 21 | func (c *CliError) Error() string { 22 | return fmt.Sprintf("%v\n\nThe command was:\n%v", c.Err, c.Cmd) 23 | } 24 | 25 | // Register standard helpers to the generator. 26 | func Register(g *emd.Generator) error { 27 | 28 | // deprecated for cat 29 | g.AddFunc("file", func(f string, exts ...string) (string, error) { 30 | s, err := ioutil.ReadFile(f) 31 | if err != nil { 32 | return "", err 33 | } 34 | ext := filepath.Ext(f) 35 | ext = strings.TrimPrefix(ext, ".") 36 | if len(exts) > 0 { 37 | ext = exts[0] 38 | } 39 | 40 | log.Printf("file function is deprecated , please update to: {{cat %q | color %q}}", f, ext) 41 | 42 | res := ` 43 | ###### > ` + f + ` 44 | ` + "```" + ext + ` 45 | ` + strings.TrimSpace(string(s)) + ` 46 | ` + "```" 47 | return res, err 48 | }) 49 | 50 | // deprecated for exec 51 | g.AddFunc("cli", func(bin string, args ...string) (string, error) { 52 | d := "\"" + strings.Join(args, "\" \"") + "\"" 53 | log.Printf("cli function is deprecated , please update to: {{exec %q %v | color \"sh\"}}", bin, d) 54 | 55 | cmd := exec.Command(bin, args...) 56 | out, err := cmd.CombinedOutput() 57 | if err != nil { 58 | s := bin 59 | for _, a := range args { 60 | if strings.Index(a, "\"") > -1 { 61 | a = strings.Replace(a, "\"", "\\\"", -1) 62 | } 63 | s += fmt.Sprintf(" %v", a) 64 | } 65 | return "", &CliError{Err: err, Cmd: s} 66 | } 67 | 68 | fbin := filepath.Base(bin) 69 | res := ` 70 | ###### $ ` + fbin + ` ` + strings.Join(args, " ") + ` 71 | ` + "```sh" + ` 72 | ` + strings.TrimSpace(string(out)) + ` 73 | ` + "```" 74 | return res, err 75 | }) 76 | 77 | return nil 78 | } 79 | -------------------------------------------------------------------------------- /emd/emd.go: -------------------------------------------------------------------------------- 1 | // Package emd provides support to process .md files. 2 | package emd 3 | 4 | import ( 5 | "bytes" 6 | "fmt" 7 | "io" 8 | "io/ioutil" 9 | "text/template" 10 | 11 | "github.com/mh-cbon/emd/utils" 12 | ) 13 | 14 | //Generator generates an emd content. 15 | type Generator struct { 16 | t *template.Template 17 | o io.Writer 18 | tpls []string 19 | funcs map[string]interface{} 20 | data map[string]interface{} 21 | post []func(string) string 22 | } 23 | 24 | // NewGenerator creates Generator Pointers. 25 | func NewGenerator() *Generator { 26 | return &Generator{ 27 | tpls: []string{}, 28 | funcs: map[string]interface{}{}, 29 | data: map[string]interface{}{}, 30 | } 31 | } 32 | 33 | //AddPostProcess registers a post process function. 34 | // Post process are registered by template func call and are removed after next template generation. 35 | func (g *Generator) AddPostProcess(f func(string) string) { 36 | g.post = append(g.post, f) 37 | } 38 | 39 | //AddFunc registers a template function. 40 | func (g *Generator) AddFunc(name string, f interface{}) { 41 | g.funcs[name] = f 42 | } 43 | 44 | //AddFuncs registers a map of template functions. 45 | func (g *Generator) AddFuncs(fm map[string]interface{}) { 46 | for name, f := range fm { 47 | g.funcs[name] = f 48 | } 49 | } 50 | 51 | //SetData registers a template data. 52 | func (g *Generator) SetData(name string, d interface{}) { 53 | g.data[name] = d 54 | } 55 | 56 | //SetDataMap registers a map of template data. 57 | func (g *Generator) SetDataMap(dm map[string]interface{}) { 58 | for name, d := range dm { 59 | g.data[name] = d 60 | } 61 | } 62 | 63 | //AddTemplate registers a template string. 64 | func (g *Generator) AddTemplate(t string) { 65 | // read the prelude 66 | newT, newData, err := utils.GetPrelude(t) 67 | if err != nil { 68 | panic(err) 69 | } 70 | g.SetDataMap(newData) 71 | 72 | g.tpls = append(g.tpls, newT) 73 | } 74 | 75 | //AddFileTemplate registers a template file. 76 | func (g *Generator) AddFileTemplate(t string) error { 77 | s, err := ioutil.ReadFile(t) 78 | if err != nil { 79 | return err 80 | } 81 | // add the template string. 82 | g.AddTemplate(string(s)) 83 | return nil 84 | } 85 | 86 | //GetTemplate returns the compiled templates. 87 | //It is available only during Execute. 88 | func (g *Generator) GetTemplate() *template.Template { 89 | return g.t 90 | } 91 | 92 | //GetOut returns the out writer. 93 | //It is available only during Execute. 94 | func (g *Generator) GetOut() io.Writer { 95 | return g.o 96 | } 97 | 98 | //WriteString writes a string on out. 99 | //It is available only during Execute. 100 | func (g *Generator) WriteString(s string) (int, error) { 101 | return g.o.Write([]byte(s)) 102 | } 103 | 104 | //GetData returns a copy of the template's data. 105 | //It is available only during Execute. 106 | func (g *Generator) GetData() map[string]interface{} { 107 | ret := map[string]interface{}{} 108 | for k, v := range g.data { 109 | ret[k] = v 110 | } 111 | return ret 112 | } 113 | 114 | //GetKey returns value of K. 115 | func (g *Generator) GetKey(K string) interface{} { 116 | return g.data[K] 117 | } 118 | 119 | //GetSKey returns a string value of K. 120 | func (g *Generator) GetSKey(K string) string { 121 | v := g.GetKey(K) 122 | if v == nil { 123 | return "" 124 | } 125 | return fmt.Sprintf("%v", g.GetKey(K)) 126 | } 127 | 128 | //Execute the template to out. 129 | func (g *Generator) Execute(out io.Writer) error { 130 | var b bytes.Buffer 131 | g.o = &b // becasue of post process, we need to buffer out. 132 | var err error 133 | g.t = template.New("").Funcs(g.funcs) 134 | for _, tpl := range g.tpls { 135 | g.t, err = g.t.Parse(tpl) 136 | if err != nil { 137 | return err 138 | } 139 | } 140 | err = g.t.Execute(g.o, g.data) 141 | g.t = nil 142 | g.o = nil 143 | s := b.String() 144 | b.Truncate(0) 145 | for _, p := range g.post { 146 | s = p(s) 147 | } 148 | g.post = g.post[:0] 149 | out.Write([]byte(s)) 150 | return err 151 | } 152 | -------------------------------------------------------------------------------- /emd/emd_test.go: -------------------------------------------------------------------------------- 1 | package emd 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestVVV(t *testing.T) { 10 | gen := NewGenerator() 11 | gen.AddTemplate(`The result of the func: {{f "s"}} 12 | The result of the template: {{template "s" .}} 13 | The data: {{.}} 14 | `) 15 | gen.AddTemplate(`{{define "s"}}s template{{end}}`) 16 | gen.AddFunc("f", func(s string) string { return strings.ToUpper(s) }) 17 | gen.SetData("the", "data") 18 | gen.AddPostProcess(func(s string) string { 19 | return s + "\npostprocess" 20 | }) 21 | var buf bytes.Buffer 22 | gen.Execute(&buf) 23 | 24 | expectedOut := `The result of the func: S 25 | The result of the template: s template 26 | The data: map[the:data] 27 | 28 | postprocess` 29 | gotOut := buf.String() 30 | if expectedOut != gotOut { 31 | t.Errorf( 32 | "Unexpected result\n==== expected\n%q\n==== got\n%v", 33 | expectedOut, 34 | gotOut, 35 | ) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /glide.lock: -------------------------------------------------------------------------------- 1 | hash: d8b138bdb5a5e0c112e455dbaadb27b60defda234fa7cb2da858c734a15b0118 2 | updated: 2017-05-08T14:18:10.415313664+02:00 3 | imports: 4 | - name: gopkg.in/yaml.v2 5 | version: cd8b52f8269e0feb286dfeef29f8fe4d5b397e0b 6 | testImports: [] 7 | -------------------------------------------------------------------------------- /glide.yaml: -------------------------------------------------------------------------------- 1 | package: github.com/mh-cbon/emd 2 | import: 3 | - package: gopkg.in/yaml.v2 4 | -------------------------------------------------------------------------------- /go-nonstd/emd.go: -------------------------------------------------------------------------------- 1 | // Package gononstd contains go non-standard helpers. 2 | package gononstd 3 | 4 | import "github.com/mh-cbon/emd/emd" 5 | 6 | // InstructionGlideInstall is a template to show instructions to install the package with glide. 7 | var InstructionGlideInstall = `{{define "glide/install" -}} 8 | ` + "```sh" + ` 9 | mkdir -p $GOPATH/src/{{.URL}} 10 | cd $GOPATH/src/{{.URL}} 11 | git clone https://{{.URL}}.git . 12 | glide install 13 | go install 14 | ` + "```" + ` 15 | {{- end}}` 16 | 17 | // Register go non-standard helpers to the generator. 18 | func Register(g *emd.Generator) error { 19 | g.AddTemplate(InstructionGlideInstall) 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /go/emd.go: -------------------------------------------------------------------------------- 1 | // Package gostd contains go standard helpers. 2 | package gostd 3 | 4 | import ( 5 | "fmt" 6 | "go/parser" 7 | "go/token" 8 | "os" 9 | "path/filepath" 10 | "strings" 11 | 12 | "github.com/mh-cbon/emd/emd" 13 | "github.com/mh-cbon/emd/utils" 14 | ) 15 | 16 | // PkgDoc Reads the first of the files, or `main.go`, lookup for its package comment and returns it as plain text. 17 | func PkgDoc(files ...string) (string, error) { 18 | file := "main.go" 19 | if len(files) > 0 { 20 | file = files[0] 21 | } 22 | if _, err := os.Stat(file); len(files) == 0 && os.IsNotExist(err) { 23 | return "", nil 24 | } 25 | 26 | fset := token.NewFileSet() 27 | f, err := parser.ParseFile(fset, file, nil, parser.ParseComments) 28 | if err != nil { 29 | return "", fmt.Errorf("Failed to parse input file: %v", err) 30 | } 31 | 32 | if f.Comments == nil || len(f.Comments) == 0 { 33 | return "Go package documentation not found!", nil 34 | } 35 | 36 | return f.Comments[0].Text(), nil 37 | } 38 | 39 | // GoTest Reads the first of the files, or `main.go`, lookup for its package comment and returns it as plain text. 40 | func GoTest(g *emd.Generator) func(string, string, ...string) (string, error) { 41 | return func(rpkg, run string, args ...string) (string, error) { 42 | if rpkg != "" { 43 | if _, err := os.Stat(rpkg); !os.IsNotExist(err) { 44 | rpkg, err = filepath.Abs(rpkg) 45 | if err != nil { 46 | return "", err 47 | } 48 | GOPATH := filepath.Join(os.Getenv("GOPATH"), "src") 49 | rpkg = strings.Replace(rpkg, GOPATH, "", -1) 50 | rpkg = strings.Replace(rpkg, "\\", "/", -1) // windows.. 51 | } 52 | } 53 | nargs := []string{"test", "-v"} 54 | if rpkg != "" { 55 | nargs = append(nargs, rpkg[1:]) // rm front / 56 | } 57 | if run != "" { 58 | nargs = append(nargs, []string{"-run", run}...) 59 | } 60 | nargs = append(nargs, args...) 61 | out, err := utils.Exec("go", nargs) 62 | if err != nil { 63 | return "", err 64 | } 65 | 66 | s := utils.GetCmdStr("go", nargs) 67 | pre := g.GetSKey("emd_gotest_pre") 68 | _, err = g.WriteString(pre + s + "\n") 69 | 70 | return strings.TrimSpace(string(out)), err 71 | } 72 | } 73 | 74 | // InstructionGoGetInstall is a template to show instructions to install the package with go get. 75 | var InstructionGoGetInstall = `{{define "go/install" -}} 76 | ` + "```sh" + ` 77 | go get {{.ProviderURL}}/{{.User}}/{{.Name}} 78 | ` + "```" + ` 79 | {{- end}}` 80 | 81 | // BadgeGoDoc is a template to show a godoc badge. 82 | var BadgeGoDoc = `{{define "badge/godoc" -}} 83 | [!` + 84 | `[GoDoc]` + 85 | `(https://godoc.org/{{.ProviderURL}}/{{.User}}/{{.Name}}?status.svg)` + 86 | `]` + 87 | `(http://godoc.org/{{.ProviderURL}}/{{.User}}/{{.Name}}) 88 | {{- end}}` 89 | 90 | // BadgeGoReport is a template to show a goreport badge. 91 | var BadgeGoReport = `{{define "badge/goreport" -}} 92 | [!` + 93 | `[Go Report Card]` + 94 | `(https://goreportcard.com/badge/{{.ProviderURL}}/{{.User}}/{{.Name}})` + 95 | `]` + 96 | `(https://goreportcard.com/report/{{.ProviderURL}}/{{.User}}/{{.Name}}) 97 | {{- end}}` 98 | 99 | // Register go standard helpers to the generator. 100 | func Register(g *emd.Generator) error { 101 | g.AddFunc("pkgdoc", PkgDoc) 102 | g.AddFunc("gotest", GoTest(g)) 103 | g.AddTemplate(InstructionGoGetInstall) 104 | g.AddTemplate(BadgeGoDoc) 105 | g.AddTemplate(BadgeGoReport) 106 | return nil 107 | } 108 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Enhanced Markdown template processor. 2 | package main 3 | 4 | import ( 5 | "bytes" 6 | "encoding/json" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | "path/filepath" 13 | "regexp" 14 | "time" 15 | 16 | "github.com/mh-cbon/emd/cli" 17 | "github.com/mh-cbon/emd/deprecated" 18 | "github.com/mh-cbon/emd/emd" 19 | gostd "github.com/mh-cbon/emd/go" 20 | gononstd "github.com/mh-cbon/emd/go-nonstd" 21 | "github.com/mh-cbon/emd/provider" 22 | "github.com/mh-cbon/emd/std" 23 | ) 24 | 25 | // VERSION defines the running build id. 26 | var VERSION = "0.0.0" 27 | 28 | var program = cli.NewProgram("emd", VERSION) 29 | 30 | var verbose bool 31 | 32 | func logMsg(f string, args ...interface{}) { 33 | if verbose { 34 | log.Printf(f+"\n", args...) 35 | } 36 | } 37 | 38 | func main() { 39 | program.Bind() 40 | if err := program.Run(os.Args); err != nil { 41 | log.Println(err) 42 | os.Exit(1) 43 | } 44 | } 45 | 46 | // gen sub command 47 | type gencommand struct { 48 | *cli.Command 49 | in string 50 | out string 51 | data string 52 | help bool 53 | shortHelp bool 54 | } 55 | 56 | // init sub command 57 | type initcommand struct { 58 | *cli.Command 59 | help bool 60 | shortHelp bool 61 | out string 62 | force bool 63 | } 64 | 65 | func init() { 66 | gen := &gencommand{Command: cli.NewCommand("gen", "Process an emd file.", Generate)} 67 | gen.Set.StringVar(&gen.in, "in", "", "Input src file") 68 | gen.Set.StringVar(&gen.out, "out", "-", "Output destination, defaults to stdout") 69 | gen.Set.StringVar(&gen.data, "data", "", "JSON map of data") 70 | gen.Set.BoolVar(&gen.help, "help", false, "Show help") 71 | gen.Set.BoolVar(&gen.shortHelp, "h", false, "Show help") 72 | program.Add(gen) 73 | 74 | ini := &initcommand{Command: cli.NewCommand("init", "Init a basic emd file.", InitFile)} 75 | ini.Set.BoolVar(&ini.help, "help", false, "Show help") 76 | ini.Set.BoolVar(&ini.shortHelp, "h", false, "Show help") 77 | ini.Set.BoolVar(&ini.force, "force", false, "Force write") 78 | ini.Set.StringVar(&ini.out, "out", "README.e.md", "Out file") 79 | program.Add(ini) 80 | 81 | verbose = os.Getenv("VERBOSE") != "" 82 | } 83 | 84 | // Generate is the cli command implementation of gen. 85 | func Generate(s cli.Commander) error { 86 | 87 | cmd, ok := s.(*gencommand) 88 | if ok == false { 89 | return fmt.Errorf("Invalid command type %T", s) 90 | } 91 | 92 | if cmd.help || cmd.shortHelp { 93 | return program.ShowCmdUsage(cmd) 94 | } 95 | 96 | out, err := getStdout(cmd.out) 97 | if err != nil { 98 | return err 99 | } 100 | if x, ok := out.(io.Closer); ok { 101 | defer x.Close() 102 | } 103 | 104 | projectPath, err := getProjectPath() 105 | if err != nil { 106 | return err 107 | } 108 | logMsg("projectPath %q", projectPath) 109 | 110 | plugins := getPlugins() 111 | data, err := getData(projectPath) 112 | if err != nil { 113 | return err 114 | } 115 | 116 | gen := emd.NewGenerator() 117 | 118 | gen.SetDataMap(data) 119 | 120 | if cmd.in != "" { 121 | if err := gen.AddFileTemplate(cmd.in); err != nil { 122 | return err 123 | } 124 | } else { 125 | b := tryReadOsStdin() 126 | if b != nil && b.Len() > 0 { 127 | gen.AddTemplate(b.String()) 128 | 129 | } else { 130 | if s, err := os.Stat("README.e.md"); !os.IsNotExist(err) && s.IsDir() == false { 131 | err := gen.AddFileTemplate("README.e.md") 132 | if err != nil { 133 | return err 134 | } 135 | } else { 136 | gen.AddTemplate(defTemplate) 137 | } 138 | } 139 | } 140 | 141 | for name, plugin := range plugins { 142 | if err := plugin(gen); err != nil { 143 | return fmt.Errorf("Failed to register %v package: %v", name, err) 144 | } 145 | } 146 | 147 | if cmd.data != "" { 148 | jData := map[string]interface{}{} 149 | if err := json.Unmarshal([]byte(cmd.data), &jData); err != nil { 150 | return fmt.Errorf("Cannot decode JSON data string: %v", err) 151 | } 152 | gen.SetDataMap(jData) 153 | } 154 | 155 | if err := gen.Execute(out); err != nil { 156 | return fmt.Errorf("Generator failed: %v", err) 157 | } 158 | 159 | return nil 160 | } 161 | 162 | func tryReadOsStdin() *bytes.Buffer { 163 | copied := make(chan bool) 164 | timedout := make(chan bool) 165 | var ret bytes.Buffer 166 | go func() { 167 | io.Copy(&ret, os.Stdin) 168 | copied <- true 169 | }() 170 | go func() { 171 | <-time.After(time.Millisecond * 10) 172 | timedout <- ret.Len() == 0 173 | }() 174 | select { 175 | case empty := <-timedout: 176 | if empty { 177 | return nil 178 | } 179 | <-copied 180 | case <-copied: 181 | } 182 | return &ret 183 | } 184 | 185 | func getProjectPath() (string, error) { 186 | originalCwd, err := os.Getwd() 187 | if err != nil { 188 | return "", err 189 | } 190 | logMsg("cwd %q", originalCwd) 191 | 192 | // regular go package 193 | { 194 | projectPath, err := matchProjectPath(originalCwd) 195 | if err == nil { 196 | return projectPath, nil 197 | } 198 | } 199 | 200 | // symlinked go package 201 | { 202 | cwd, err := filepath.EvalSymlinks(originalCwd) 203 | if err == nil { 204 | projectPath, err := matchProjectPath(cwd) 205 | if err == nil { 206 | return projectPath, nil 207 | } 208 | } 209 | } 210 | 211 | // all other cases 212 | return originalCwd, nil 213 | } 214 | 215 | var re = regexp.MustCompile("(src/[^/]+[.](com|org|net)/.+)") 216 | 217 | func matchProjectPath(p string) (string, error) { 218 | res := re.FindAllString(p, -1) 219 | if len(res) > 0 { 220 | return res[0][3:], nil 221 | } 222 | return "", fmt.Errorf("Invalid working directory %q", p) 223 | } 224 | 225 | func getData(cwd string) (map[string]interface{}, error) { 226 | p := provider.Default(cwd) 227 | return map[string]interface{}{ 228 | "Name": p.GetProjectName(), 229 | "User": p.GetUserName(), 230 | "ProviderURL": p.GetProviderURL(), 231 | "ProviderName": p.GetProviderID(), 232 | "URL": p.GetURL(), 233 | "ProjectURL": p.GetProjectURL(), 234 | "Branch": "master", 235 | }, nil 236 | } 237 | 238 | func getPlugins() map[string]func(*emd.Generator) error { 239 | return map[string]func(*emd.Generator) error{ 240 | "std": std.Register, 241 | "gostd": gostd.Register, 242 | "gononstd": gononstd.Register, 243 | "deprecated": deprecated.Register, 244 | } 245 | } 246 | 247 | func getStdout(out string) (io.Writer, error) { 248 | ret := os.Stdout 249 | 250 | if out != "-" { 251 | f, err := os.Create(out) 252 | if err != nil { 253 | f, err = os.Open(out) 254 | if err != nil { 255 | return nil, fmt.Errorf("Cannot open out destination: %v", err) 256 | } 257 | } 258 | ret = f 259 | } 260 | return ret, nil 261 | } 262 | 263 | // InitFile creates a basic emd file if none exists. 264 | func InitFile(s cli.Commander) error { 265 | 266 | cmd, ok := s.(*initcommand) 267 | if ok == false { 268 | return fmt.Errorf("Invalid command type %T", s) 269 | } 270 | 271 | if cmd.help || cmd.shortHelp { 272 | return program.ShowCmdUsage(cmd) 273 | } 274 | 275 | out := cmd.out 276 | if cmd.out == "" { 277 | out = "README.e.md" 278 | } 279 | if _, err := os.Stat(out); !cmd.force && !os.IsNotExist(err) { 280 | return fmt.Errorf("File exists at %q", out) 281 | } 282 | return ioutil.WriteFile(out, []byte(defTemplate), os.ModePerm) 283 | } 284 | 285 | var defTemplate = `# {{.Name}} 286 | 287 | {{template "badge/goreport" .}} {{template "badge/godoc" .}} 288 | 289 | {{pkgdoc}} 290 | 291 | # {{toc 5}} 292 | 293 | # Install 294 | 295 | {{template "gh/releases" .}} 296 | 297 | #### go 298 | {{template "go/install" .}} 299 | ` 300 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package main_test 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/mh-cbon/emd/emd" 7 | "github.com/mh-cbon/emd/std" 8 | ) 9 | 10 | var projectName = "dummy" 11 | 12 | // ExampleGenerate demonstrates the generation 13 | // of the given README.e.md source file 14 | // to os.Stdout. 15 | func Example() { 16 | 17 | // make a new instance of emd.Generator. 18 | gen := emd.NewGenerator() 19 | 20 | // set the main template. 21 | gen.AddTemplate("{{.Name}}") 22 | 23 | // set the data available in templates. 24 | gen.SetDataMap(map[string]interface{}{"Name": projectName}) 25 | 26 | // register a plugin 27 | if err := std.Register(gen); err != nil { 28 | panic(err) 29 | } 30 | 31 | // process the template. 32 | if err := gen.Execute(os.Stdout); err != nil { 33 | panic(err) 34 | } 35 | // Output: dummy 36 | } 37 | -------------------------------------------------------------------------------- /provider/provider.go: -------------------------------------------------------------------------------- 1 | package provider 2 | 3 | import ( 4 | "github.com/mh-cbon/emd/provider/std" 5 | ) 6 | 7 | //Provider identify an url. 8 | type Provider interface { 9 | Is(string) bool 10 | SetURL(string) 11 | GetUserName() string 12 | GetProjectName() string 13 | GetProjectPath() string 14 | GetProviderURL() string 15 | GetProviderID() string 16 | GetURL() string 17 | GetProjectURL() string 18 | } 19 | 20 | // Providers if a facade of many Provider. 21 | type Providers struct { 22 | URL string 23 | Providers []Provider 24 | } 25 | 26 | // New providers for url. 27 | func New(url string) Providers { 28 | return Providers{URL: url} 29 | } 30 | 31 | // Default makes a new Providers facade with pre loaded gh provider. 32 | func Default(url string) Providers { 33 | return New(url).Add(std.New()) 34 | } 35 | 36 | // Add a concrete provider. 37 | func (p Providers) Add(provider ...Provider) Providers { 38 | p.Providers = append(p.Providers, provider...) 39 | return p 40 | } 41 | 42 | // Match tells if an url matches a provider. 43 | func (p Providers) Match() bool { 44 | for _, pp := range p.Providers { 45 | if pp.Is(p.URL) { 46 | return true 47 | } 48 | } 49 | return false 50 | } 51 | 52 | func (p Providers) selectProvider() Provider { 53 | ret := &NotFoundProvider{} 54 | for _, pp := range p.Providers { 55 | if pp.Is(p.URL) { 56 | pp.SetURL(p.URL) 57 | return pp 58 | } 59 | } 60 | return ret 61 | } 62 | 63 | // GetUserName of the the current url. 64 | func (p Providers) GetUserName() string { 65 | return p.selectProvider().GetUserName() 66 | } 67 | 68 | // GetProjectName of the the current url. 69 | func (p Providers) GetProjectName() string { 70 | return p.selectProvider().GetProjectName() 71 | } 72 | 73 | // GetProviderURL of the the current url. 74 | func (p Providers) GetProviderURL() string { 75 | return p.selectProvider().GetProviderURL() 76 | } 77 | 78 | // GetProviderID of the the current url. 79 | func (p Providers) GetProviderID() string { 80 | return p.selectProvider().GetProviderID() 81 | } 82 | 83 | // GetProjectPath of the the current url. 84 | func (p Providers) GetProjectPath() string { 85 | return p.selectProvider().GetProjectPath() 86 | } 87 | 88 | // GetURL of the the current url. 89 | func (p Providers) GetURL() string { 90 | return p.selectProvider().GetURL() 91 | } 92 | 93 | // GetProjectURL of the the current url. 94 | func (p Providers) GetProjectURL() string { 95 | return p.selectProvider().GetProjectURL() 96 | } 97 | 98 | // NotFoundProvider for an url not identified 99 | type NotFoundProvider struct{} 100 | 101 | var notFound = "not found" 102 | 103 | // Is always return false. 104 | func (p *NotFoundProvider) Is(u string) bool { 105 | return false 106 | } 107 | 108 | // SetURL si no op. 109 | func (p *NotFoundProvider) SetURL(u string) { 110 | } 111 | 112 | // GetUserName of the the current url. 113 | func (p *NotFoundProvider) GetUserName() string { 114 | return notFound 115 | } 116 | 117 | // GetProjectName of the the current url. 118 | func (p *NotFoundProvider) GetProjectName() string { 119 | return notFound 120 | } 121 | 122 | // GetProviderURL of the the current url. 123 | func (p *NotFoundProvider) GetProviderURL() string { 124 | return notFound 125 | } 126 | 127 | // GetProviderID of the the current url. 128 | func (p *NotFoundProvider) GetProviderID() string { 129 | return notFound 130 | } 131 | 132 | // GetProjectPath of the the current url. 133 | func (p *NotFoundProvider) GetProjectPath() string { 134 | return notFound 135 | } 136 | 137 | // GetProjectURL of the the current url. 138 | func (p *NotFoundProvider) GetProjectURL() string { 139 | return notFound 140 | } 141 | 142 | // GetURL of the the current url. 143 | func (p *NotFoundProvider) GetURL() string { 144 | return notFound 145 | } 146 | -------------------------------------------------------------------------------- /provider/std/provider.go: -------------------------------------------------------------------------------- 1 | package std 2 | 3 | import ( 4 | "regexp" 5 | "strings" 6 | ) 7 | 8 | var re = regexp.MustCompile("^([^/]+)[.](com|org|net)/.*") 9 | 10 | // Provider detects GH url 11 | type Provider struct { 12 | URL string 13 | providerURL string 14 | providerID string 15 | } 16 | 17 | // New provider to work with url 18 | func New() *Provider { 19 | return &Provider{} 20 | } 21 | 22 | // Is the current url about gh? 23 | func (p *Provider) Is(url string) bool { 24 | x := cleanURL(url) 25 | return re.MatchString(x) 26 | } 27 | 28 | // SetURL of the provider 29 | func (p *Provider) SetURL(url string) { 30 | p.URL = cleanURL(url) 31 | parts := strings.Split(p.URL, "/") 32 | p.providerURL = parts[0] 33 | parts2 := re.FindAllStringSubmatch(p.URL, -1) 34 | if len(parts2) > 0 { 35 | p.providerID = parts2[0][1] 36 | } 37 | } 38 | 39 | func cleanURL(url string) string { 40 | if url[:4] == "http" { 41 | url = url[4:] 42 | } else if url[:5] == "https" { 43 | url = url[5:] 44 | } 45 | if url[:3] == "://" { 46 | url = url[3:] 47 | } 48 | if url[:1] == "/" { 49 | url = url[1:] 50 | } 51 | return url 52 | } 53 | 54 | // GetUserName of the the current url. 55 | func (p *Provider) GetUserName() string { 56 | ss := strings.Split(p.URL, "/") 57 | if len(ss) > 1 { 58 | return ss[1] 59 | } 60 | return "" 61 | } 62 | 63 | // GetProjectName of the the current url. 64 | func (p *Provider) GetProjectName() string { 65 | ss := strings.Split(p.URL, "/") 66 | if len(ss) > 2 { 67 | return ss[2] 68 | } 69 | return "" 70 | } 71 | 72 | // GetProjectPath of the the current url. 73 | func (p *Provider) GetProjectPath() string { 74 | ss := strings.Split(p.URL, "/") 75 | if len(ss) > 3 { 76 | return "/" + strings.Join(ss[3:], "/") 77 | } 78 | return "" 79 | } 80 | 81 | // GetProjectURL of the the current url. 82 | func (p *Provider) GetProjectURL() string { 83 | ret := []string{} 84 | if x := p.GetProviderURL(); x != "" { 85 | if y := p.GetUserName(); y != "" { 86 | if z := p.GetProjectName(); z != "" { 87 | ret = append(ret, x) 88 | ret = append(ret, y) 89 | ret = append(ret, z) 90 | } 91 | } 92 | } 93 | return strings.Join(ret, "/") 94 | } 95 | 96 | // GetURL of the the current url. 97 | func (p *Provider) GetURL() string { 98 | ret := []string{} 99 | if x := p.GetProjectURL(); x != "" { 100 | ret = append(ret, x) 101 | if y := p.GetProjectPath(); y != "" { 102 | ret = append(ret, y[1:]) //rm front / 103 | } 104 | } 105 | return strings.Join(ret, "/") 106 | } 107 | 108 | // GetProviderURL of the the current url. 109 | func (p *Provider) GetProviderURL() string { 110 | return p.providerURL 111 | } 112 | 113 | // GetProviderID of the the current url. 114 | func (p *Provider) GetProviderID() string { 115 | return p.providerID 116 | } 117 | -------------------------------------------------------------------------------- /provider/std/provider_test.go: -------------------------------------------------------------------------------- 1 | package std 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestIs(t *testing.T) { 8 | data := []string{ 9 | "http://github.com/", 10 | } 11 | p := New() 12 | want := true 13 | for i, url := range data { 14 | got := p.Is(url) 15 | if got != want { 16 | t.Errorf("At %v want: %v got %v in %q", i, want, got, url) 17 | } 18 | } 19 | } 20 | 21 | func TestIsNot(t *testing.T) { 22 | data := []string{ 23 | "qsdsqdsqdqs", 24 | } 25 | p := New() 26 | want := false 27 | for i, url := range data { 28 | got := p.Is(url) 29 | if got != want { 30 | t.Errorf("At %v want: %v got %v in %q", i, want, got, url) 31 | } 32 | } 33 | } 34 | 35 | func TestGetUserName(t *testing.T) { 36 | data := map[string]string{ 37 | "http://github.com/": "", 38 | "http://github.com/mh-cbon": "mh-cbon", 39 | "http://github.com/mh-cbon/emd": "mh-cbon", 40 | "http://github.com/mh-cbon/emd/cmd": "mh-cbon", 41 | "github.com/mh-cbon": "mh-cbon", 42 | "github.com/mh-cbon/emd": "mh-cbon", 43 | "github.com/mh-cbon/emd/cmd": "mh-cbon", 44 | "/github.com/mh-cbon": "mh-cbon", 45 | "/github.com/mh-cbon/emd": "mh-cbon", 46 | "/github.com/mh-cbon/emd/cmd": "mh-cbon", 47 | } 48 | p := New() 49 | for url, want := range data { 50 | p.SetURL(url) 51 | got := p.GetUserName() 52 | if got != want { 53 | t.Errorf("Want: %q got %q in %q", want, got, url) 54 | } 55 | } 56 | } 57 | 58 | func TestGetProjectName(t *testing.T) { 59 | data := map[string]string{ 60 | "http://github.com/": "", 61 | "http://github.com/mh-cbon": "", 62 | "http://github.com/mh-cbon/emd": "emd", 63 | "http://github.com/mh-cbon/emd/cmd": "emd", 64 | "github.com/mh-cbon": "", 65 | "github.com/mh-cbon/emd": "emd", 66 | "github.com/mh-cbon/emd/cmd": "emd", 67 | "/github.com/mh-cbon": "", 68 | "/github.com/mh-cbon/emd": "emd", 69 | "/github.com/mh-cbon/emd/cmd": "emd", 70 | } 71 | p := New() 72 | for url, want := range data { 73 | p.SetURL(url) 74 | got := p.GetProjectName() 75 | if got != want { 76 | t.Errorf("Want: %q got %q in %q", want, got, url) 77 | } 78 | } 79 | } 80 | 81 | func TestGetProjectPath(t *testing.T) { 82 | data := map[string]string{ 83 | "http://github.com/": "", 84 | "http://github.com/mh-cbon": "", 85 | "http://github.com/mh-cbon/emd": "", 86 | "http://github.com/mh-cbon/emd/cmd": "/cmd", 87 | "github.com/mh-cbon": "", 88 | "github.com/mh-cbon/emd": "", 89 | "github.com/mh-cbon/emd/cmd": "/cmd", 90 | "/github.com/mh-cbon": "", 91 | "/github.com/mh-cbon/emd": "", 92 | "/github.com/mh-cbon/emd/cmd": "/cmd", 93 | } 94 | p := New() 95 | for url, want := range data { 96 | p.SetURL(url) 97 | got := p.GetProjectPath() 98 | if got != want { 99 | t.Errorf("Want: %q got %q in %q", want, got, url) 100 | } 101 | } 102 | } 103 | 104 | func TestGetProjectURL(t *testing.T) { 105 | data := map[string]string{ 106 | "http://github.com/": "", 107 | "http://github.com/mh-cbon": "", 108 | "http://github.com/mh-cbon/emd": "github.com/mh-cbon/emd", 109 | "http://github.com/mh-cbon/emd/cmd": "github.com/mh-cbon/emd", 110 | "github.com/mh-cbon": "", 111 | "github.com/mh-cbon/emd": "github.com/mh-cbon/emd", 112 | "github.com/mh-cbon/emd/cmd": "github.com/mh-cbon/emd", 113 | "/github.com/mh-cbon": "", 114 | "/github.com/mh-cbon/emd": "github.com/mh-cbon/emd", 115 | "/github.com/mh-cbon/emd/cmd": "github.com/mh-cbon/emd", 116 | } 117 | p := New() 118 | for url, want := range data { 119 | p.SetURL(url) 120 | got := p.GetProjectURL() 121 | if got != want { 122 | t.Errorf("Want: %q got %q in %q", want, got, url) 123 | } 124 | } 125 | } 126 | 127 | func TestGetProviderID(t *testing.T) { 128 | data := map[string]string{ 129 | "http://github.com/": "github", 130 | "http://github.com/mh-cbon": "github", 131 | "http://github.com/mh-cbon/emd": "github", 132 | "http://github.com/mh-cbon/emd/cmd": "github", 133 | "github.com/mh-cbon": "github", 134 | "github.com/mh-cbon/emd": "github", 135 | "github.com/mh-cbon/emd/cmd": "github", 136 | "/github.com/mh-cbon": "github", 137 | "/github.com/mh-cbon/emd": "github", 138 | "/github.com/mh-cbon/emd/cmd": "github", 139 | } 140 | p := New() 141 | for url, want := range data { 142 | p.SetURL(url) 143 | got := p.GetProviderID() 144 | if got != want { 145 | t.Errorf("Want: %q got %q in %q", want, got, url) 146 | } 147 | } 148 | } 149 | 150 | func TestGetURL(t *testing.T) { 151 | data := map[string]string{ 152 | "http://github.com/": "", 153 | "http://github.com/mh-cbon": "", 154 | "http://github.com/mh-cbon/emd": "github.com/mh-cbon/emd", 155 | "http://github.com/mh-cbon/emd/cmd": "github.com/mh-cbon/emd/cmd", 156 | "github.com/mh-cbon": "", 157 | "github.com/mh-cbon/emd": "github.com/mh-cbon/emd", 158 | "github.com/mh-cbon/emd/cmd": "github.com/mh-cbon/emd/cmd", 159 | "/github.com/mh-cbon": "", 160 | "/github.com/mh-cbon/emd": "github.com/mh-cbon/emd", 161 | "/github.com/mh-cbon/emd/cmd": "github.com/mh-cbon/emd/cmd", 162 | } 163 | p := New() 164 | for url, want := range data { 165 | p.SetURL(url) 166 | got := p.GetURL() 167 | if got != want { 168 | t.Errorf("Want: %q got %q in %q", want, got, url) 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /rpm.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "emd", 3 | "summary": "Enhanced Markdown template processor", 4 | "description": "Enhanced Markdown template processor", 5 | "changelog-cmd": "changelog rpm", 6 | "license": "LICENSE", 7 | "url": "https://github.com/mh-cbon/!name!", 8 | "files": [ 9 | { 10 | "from": "build/!arch!/!name!", 11 | "to": "%{_bindir}/", 12 | "base": "build/!arch!/", 13 | "type": "" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /std/emd.go: -------------------------------------------------------------------------------- 1 | // Package std contains standard helpers. 2 | package std 3 | 4 | import ( 5 | "fmt" 6 | "io/ioutil" 7 | "log" 8 | "strings" 9 | 10 | yaml "gopkg.in/yaml.v2" 11 | 12 | "github.com/mh-cbon/emd/emd" 13 | "github.com/mh-cbon/emd/utils" 14 | ) 15 | 16 | // Concat multiple strings. 17 | func Concat(s ...interface{}) string { 18 | ss := []string{} 19 | for _, v := range s { 20 | ss = append(ss, fmt.Sprintf("%v", v)) 21 | } 22 | return strings.Join(ss, "") 23 | } 24 | 25 | // PathJoin multiple path parts. 26 | func PathJoin(s ...interface{}) string { 27 | ss := []string{} 28 | for _, v := range s { 29 | ss = append(ss, fmt.Sprintf("%v", v)) 30 | } 31 | return strings.Join(ss, "/") 32 | } 33 | 34 | // Link creates a link according to markdown syntax. 35 | func Link(url interface{}, texts ...interface{}) string { 36 | var text interface{} 37 | text = "" 38 | if len(texts) > 0 { 39 | text = texts[0] 40 | } 41 | if text == "" { 42 | return fmt.Sprintf("%v", url) 43 | } 44 | return fmt.Sprintf("[%v](%v)", text, url) 45 | } 46 | 47 | // Img creates a img according to markdown syntax. 48 | func Img(url interface{}, alts ...interface{}) string { 49 | var alt interface{} 50 | alt = "" 51 | if len(alts) > 0 { 52 | alt = alts[0] 53 | } 54 | return fmt.Sprintf("![%v](%v)", alt, url) 55 | } 56 | 57 | // SetValue saves a value. 58 | func SetValue(g *emd.Generator) func(string, interface{}) string { 59 | return func(name string, v interface{}) string { 60 | g.SetData(name, v) 61 | return "" 62 | } 63 | } 64 | 65 | // GetValue returns a value. 66 | func GetValue(g *emd.Generator) func(string) interface{} { 67 | return func(name string) interface{} { 68 | return g.GetKey(name) 69 | } 70 | } 71 | 72 | // Cat displays a file header, returns file body. 73 | func Cat(g *emd.Generator) func(string) (string, error) { 74 | return func(f string) (string, error) { 75 | s, err := Read(f) 76 | if err != nil { 77 | return "", err 78 | } 79 | pre := g.GetSKey("emd_cat_pre") 80 | _, err = g.WriteString(pre + f + "\n") 81 | return strings.TrimSpace(string(s)), err 82 | } 83 | } 84 | 85 | // Read returns file body. 86 | func Read(f string) (string, error) { 87 | s, err := ioutil.ReadFile(f) 88 | if err != nil { 89 | return "", err 90 | } 91 | return strings.TrimSpace(string(s)), err 92 | } 93 | 94 | // makeMapOf given arguments. 95 | func makeMapOf(keyValuesMap ...interface{}) (map[string]interface{}, error) { 96 | ret := map[string]interface{}{} 97 | if len(keyValuesMap) > 0 { 98 | if len(keyValuesMap)%2 != 0 { 99 | return ret, fmt.Errorf("Incorrect arguments number in call to render template function, args are: %#v", keyValuesMap) 100 | } 101 | for i := 0; i < len(keyValuesMap); i += 2 { 102 | key, ok := keyValuesMap[i].(string) 103 | if ok == false { 104 | return ret, fmt.Errorf("Incorrect key type %T of arg %#v in call to render template function, expected a string, args are: %#v", 105 | keyValuesMap[i], 106 | keyValuesMap[i], 107 | keyValuesMap) 108 | } 109 | ret[key] = keyValuesMap[i+1] 110 | } 111 | } 112 | return ret, nil 113 | } 114 | 115 | // Render renders template wih given name, and map arguments to its data argument. 116 | func Render(g *emd.Generator) func(string, map[string]interface{}, ...interface{}) (string, error) { 117 | return func(name string, data map[string]interface{}, keyValuesMap ...interface{}) (string, error) { 118 | extraData := map[string]interface{}{} 119 | for k, v := range data { 120 | extraData[k] = v 121 | } 122 | y, err := makeMapOf(keyValuesMap...) 123 | if err != nil { 124 | return "", err 125 | } 126 | for k, v := range y { 127 | extraData[k] = v 128 | } 129 | return "", g.GetTemplate().ExecuteTemplate(g.GetOut(), name, extraData) 130 | } 131 | } 132 | 133 | // Exec display a program invokation header, returns its output. 134 | func Exec(g *emd.Generator) func(string, ...string) (string, error) { 135 | return func(bin string, args ...string) (string, error) { 136 | out, err := utils.Exec(bin, args) 137 | if err != nil { 138 | return "", err 139 | } 140 | 141 | f := utils.GetCmdStr(bin, args) 142 | pre := g.GetSKey("emd_exec_pre") 143 | _, err = g.WriteString(pre + f + "\n") 144 | 145 | return strings.TrimSpace(out), err 146 | } 147 | } 148 | 149 | // Shell display a cli invokation header, returns the cli output. 150 | func Shell(g *emd.Generator) func(string) (string, error) { 151 | return func(s string) (string, error) { 152 | out, err := utils.Shell("", s) 153 | if err != nil { 154 | return "", err 155 | } 156 | 157 | pre := g.GetSKey("emd_shell_pre") 158 | _, err = g.WriteString(pre + s + "\n") 159 | 160 | return strings.TrimSpace(string(out)), err 161 | } 162 | } 163 | 164 | // Color embeds a text block with markdown triple backquotes syntax makrup. 165 | func Color(syntax, content string) string { 166 | if content == "" && syntax != "" { 167 | content = syntax 168 | syntax = "sh" // set the default color 169 | } 170 | return fmt.Sprintf("```%v\n%v\n```", syntax, content) 171 | } 172 | 173 | // Yaml parses given file as yaml, locate given path, build a new map, yaml encode it, returns its string. 174 | func Yaml(file string, paths ...string) (string, error) { 175 | s, err := ioutil.ReadFile(file) 176 | if err != nil { 177 | return "", err 178 | } 179 | 180 | m := yaml.MapSlice{} 181 | err = yaml.Unmarshal(s, &m) 182 | if err != nil { 183 | return "", err 184 | } 185 | 186 | res := yaml.MapSlice{} 187 | if len(paths) > 0 { 188 | // make it more complex later 189 | for _, p := range paths { 190 | for _, k := range m { 191 | if k.Key.(string) == p { 192 | res = append(res, k) 193 | 194 | } 195 | } 196 | } 197 | } else { 198 | res = m 199 | } 200 | 201 | var d []byte 202 | d, err = yaml.Marshal(&res) 203 | if err != nil { 204 | return "", err 205 | } 206 | return strings.TrimSpace(string(d)), nil 207 | } 208 | 209 | // Preline prepends every line of content with pre. 210 | func Preline(pre, content string) string { 211 | res := "" 212 | for _, c := range content { 213 | res += string(c) 214 | if c == '\n' { 215 | res += pre 216 | } 217 | } 218 | if res != "" { 219 | res = pre + res 220 | } 221 | return res 222 | } 223 | 224 | // Echo prints given strings. 225 | func Echo(content ...string) string { 226 | return strings.Join(content, " ") 227 | } 228 | 229 | var replaceIndex = 0 230 | 231 | // Toc generates and prints a TOC. 232 | func Toc(g *emd.Generator) func(int, ...string) string { 233 | return func(depth int, toctitles ...string) string { 234 | toctitle := "TOC" 235 | if len(toctitles) > 0 { 236 | toctitle = toctitles[0] 237 | } 238 | replaceToken := fmt.Sprintf("%v%v", "REPLACETOKENGOESHERE", replaceIndex) 239 | replaceIndex++ 240 | 241 | g.AddPostProcess(func(s string) string { 242 | // a quick and dirty md parser of titles (###) and block (```) 243 | lineIndex := utils.LineIndex(s, replaceToken) 244 | if lineIndex > -1 { 245 | 246 | lines := strings.Split(s, "\n") 247 | 248 | titles := utils.GetAllMdTitles(s) 249 | x := titles[:0] 250 | for _, t := range titles { 251 | if t.Line > lineIndex { 252 | x = append(x, t) 253 | } 254 | } 255 | root := utils.MakeTitleTree(x) 256 | toc := "" 257 | e := -1 258 | 259 | lastPower := -1 260 | root.Traverse(utils.PowerLess(5, func(n *utils.MdTitleTree) { 261 | if n.Power < 2 { 262 | e = 0 263 | } else if lastPower < n.Power { 264 | e++ 265 | } else if lastPower > n.Power { 266 | e-- 267 | } 268 | if e < 0 { 269 | e = 0 270 | } 271 | lastPower = n.Power 272 | 273 | link := utils.GetMdLinkHash(n.Title) 274 | if n.Duplicate > -1 { 275 | link += fmt.Sprintf("-%v", n.Duplicate) 276 | } 277 | x := strings.Repeat(" ", e) 278 | toc += fmt.Sprintf("%v- [%v](#%v)\n", x, n.Title, link) 279 | })) 280 | 281 | lines[lineIndex] = strings.Replace(lines[lineIndex], replaceToken, toctitle, -1) 282 | lines = append(lines[:lineIndex+1], lines[lineIndex:]...) 283 | lines[lineIndex+1] = strings.TrimRight(toc, "\n") 284 | return strings.Join(lines, "\n") 285 | } 286 | log.Println("weird, a toc was generated, but it was not added to the final content.") 287 | return s 288 | }) 289 | return replaceToken 290 | } 291 | } 292 | 293 | // GHReleasePages is a template to show a notice about the gh releases page. 294 | var GHReleasePages = `{{define "gh/releases" -}} 295 | Check the {{link (concat "https://" .URL "/releases") "release page"}}! 296 | {{- end}}` 297 | 298 | // BadgeTravis is a template to show a travis badge. 299 | var BadgeTravis = `{{define "badge/travis" -}} 300 | {{- set "travisUrl" (pathjoin "https://travis-ci.org" .User .Name) }} 301 | {{- set "travisImg" (img (concat .travisUrl ".svg?branch=" .Branch) "travis Status") }} 302 | {{- link .travisUrl .travisImg}} 303 | {{- end}}` 304 | 305 | // BadgeAppveyor is a template to show an appveyor badge. 306 | var BadgeAppveyor = `{{define "badge/appveyor" -}} 307 | {{- set "appveyorStatusUrl" (pathjoin "https://ci.appveyor.com/api/projects/status" .ProviderName .User .Name) }} 308 | {{- set "appveyorProjectUrl" (pathjoin "https://ci.appveyor.com/project" .User .Name) }} 309 | {{- set "appveyorImg" (img (concat .appveyorStatusUrl "?branch=" .Branch "&svg=true") "Appveyor Status") }} 310 | {{- link .appveyorProjectUrl .appveyorImg }} 311 | {{- end}}` 312 | 313 | // BadgeCodeship is a template to show a codehsip badge. 314 | var BadgeCodeship = `{{define "badge/codeship" -}} 315 | {{- set "csTitle" (or .CsTitle "Codeship Status") }} 316 | {{- set "csStatusUrl" (pathjoin "https://codeship.com/projects" .CsUUID "status") }} 317 | {{- set "csProjectUrl" (pathjoin "https://codeship.com/projects" .CsProjectID) }} 318 | {{- set "csImg" (img (concat (get "csStatusUrl") "?branch=" .Branch) (get "csTitle") ) }} 319 | {{- link (get "csProjectUrl") (get "csImg") }} 320 | {{- end}}` 321 | 322 | // InstructionChocoInstall is a template to show instructions to install the package with chocolatey. 323 | var InstructionChocoInstall = `{{define "choco/install" -}} 324 | ` + "```sh" + ` 325 | choco install {{.Name}} 326 | ` + "```" + ` 327 | {{- end}}` 328 | 329 | // InstructionChocoBintrayInstall is a template to show instructions to install the package with chocolatey from bintray repo. 330 | var InstructionChocoBintrayInstall = `{{define "choco_bintray/install" -}} 331 | ` + "```sh" + ` 332 | choco source add -n={{.User}} -s="https://api.bintray.com/nuget/{{.User}}/{{or .BintrayRepo "choco"}}" 333 | choco install {{.Name}} 334 | ` + "```" + ` 335 | {{- end}}` 336 | 337 | // InstructionGhRepo is a template to show instructions to install the rpm/deb repositories with gh-pages. 338 | var InstructionGhRepo = `{{define "linux/gh_src_repo" -}} 339 | ` + "```sh" + ` 340 | wget -O - https://raw.githubusercontent.com/mh-cbon/latest/master/source.sh \ 341 | | GH={{.User}}/{{.Name}} sh -xe 342 | # or 343 | curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/source.sh \ 344 | | GH={{.User}}/{{.Name}} sh -xe 345 | ` + "```" + ` 346 | {{- end}}` 347 | 348 | // InstructionBintrayRepo is a template to show instructions to install the rpm/deb repositories via bintray. 349 | var InstructionBintrayRepo = `{{define "linux/bintray_repo" -}} 350 | ` + "```sh" + ` 351 | wget -O - https://raw.githubusercontent.com/mh-cbon/latest/master/bintray.sh \ 352 | | GH={{.User}}/{{.Name}} sh -xe 353 | # or 354 | curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/bintray.sh \ 355 | | GH={{.User}}/{{.Name}} sh -xe 356 | ` + "```" + ` 357 | {{- end}}` 358 | 359 | // InstructionGhPkg is a template to show instructions to install the rpm/deb package with gh-pages. 360 | var InstructionGhPkg = `{{define "linux/gh_pkg" -}} 361 | ` + "```sh" + ` 362 | curl -L https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \ 363 | | GH={{.User}}/{{.Name}} sh -xe 364 | # or 365 | wget -q -O - --no-check-certificate \ 366 | https://raw.githubusercontent.com/mh-cbon/latest/master/install.sh \ 367 | | GH={{.User}}/{{.Name}} sh -xe 368 | ` + "```" + ` 369 | {{- end}}` 370 | 371 | // BadgeLicense shows a badge for a license. 372 | var BadgeLicense = `{{define "license/shields" -}} 373 | {{- set "licenseFile" (or .LicenseFile "LICENSE") }} 374 | {{- set "licenseTitle" (concat .License " License") }} 375 | {{- set "licenseImg" (or .LicenseColor "blue") }} 376 | {{- set "licenseImg" (concat "License-" .License "-" (get "licenseImg") ".svg") }} 377 | {{- set "licenseImg" (concat "http://img.shields.io/badge/" (get "licenseImg")) }} 378 | {{- set "licenseImg" (img (get "licenseImg") (get "licenseTitle")) }} 379 | {{- link (get "licenseFile") (get "licenseImg") }} 380 | {{- end}}` 381 | 382 | // Register standard helpers to the generator. 383 | func Register(g *emd.Generator) error { 384 | 385 | g.AddFunc("cat", Cat(g)) 386 | g.AddFunc("read", Read) 387 | g.AddFunc("render", Render(g)) 388 | g.AddFunc("exec", Exec(g)) 389 | g.AddFunc("shell", Shell(g)) 390 | g.AddFunc("color", Color) 391 | g.AddFunc("toc", Toc(g)) 392 | g.AddFunc("yaml", Yaml) 393 | g.AddFunc("preline", Preline) 394 | g.AddFunc("echo", Echo) 395 | g.AddFunc("link", Link) 396 | g.AddFunc("img", Img) 397 | g.AddFunc("concat", Concat) 398 | g.AddFunc("pathjoin", PathJoin) 399 | g.AddFunc("set", SetValue(g)) 400 | g.AddFunc("get", GetValue(g)) 401 | 402 | g.AddTemplate(GHReleasePages) 403 | g.AddTemplate(BadgeTravis) 404 | g.AddTemplate(BadgeAppveyor) 405 | g.AddTemplate(BadgeCodeship) 406 | g.AddTemplate(InstructionChocoBintrayInstall) 407 | g.AddTemplate(InstructionChocoInstall) 408 | g.AddTemplate(InstructionGhRepo) 409 | g.AddTemplate(InstructionGhPkg) 410 | g.AddTemplate(InstructionBintrayRepo) 411 | g.AddTemplate(BadgeLicense) 412 | 413 | return nil 414 | } 415 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -ex 4 | 5 | rm $GOPATH/bin/emd 6 | go install 7 | 8 | rm -fr $GOPATH/src/github.com/mh-cbon/emd-test 9 | rm -fr ~/fake 10 | 11 | # test 1: a project contained in GOPATH, aliased out of it 12 | mkdir -p ~/fake/src/github.com/mh-cbon/test-emd 13 | ln -s ~/fake/src/github.com/mh-cbon/test-emd $GOPATH/src/github.com/mh-cbon/emd-test 14 | 15 | cd ~/fake/src/github.com/mh-cbon/test-emd 16 | 17 | export VERBOSE=y 18 | 19 | cat <> README.e.md 180 | templated 181 | EOT 182 | emd gen | grep "templated" || exit 1; 183 | 184 | cd ~ 185 | rm -fr $GOPATH/src/github.com/mh-cbon/emd-test 186 | rm -fr ~/fake 187 | 188 | # test4: ensure emd gen defaults to defTemplate if not any README.e.md . 189 | mkdir -p $GOPATH/src/github.com/mh-cbon/emd-test 190 | cd $GOPATH/src/github.com/mh-cbon/emd-test 191 | 192 | emd gen | grep "# emd-test" || exit 1; 193 | 194 | # test prelude data. 195 | cat < -1 { 24 | a = strings.Replace(a, "\"", "\\\"", -1) 25 | } 26 | s += fmt.Sprintf(" %v", a) 27 | } 28 | return s 29 | } 30 | 31 | // Exec a command 32 | func Exec(bin string, args []string) (string, error) { 33 | cmd := exec.Command(bin, args...) 34 | out, err := cmd.CombinedOutput() 35 | if err != nil { 36 | return "", &CliError{Err: err, Cmd: GetCmdStr(bin, args)} 37 | } 38 | return string(out), nil 39 | } 40 | -------------------------------------------------------------------------------- /utils/md.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | "strings" 7 | ) 8 | 9 | // LineIndex returns line index of search in content. 10 | func LineIndex(content string, search string) int { 11 | ret := -1 12 | line := "" 13 | for _, c := range content { 14 | if c == '\n' { 15 | line += "" 16 | ret++ 17 | } else { 18 | line += string(c) 19 | } 20 | if strings.Index(line, search) > -1 { 21 | ret++ 22 | break 23 | } 24 | } 25 | return ret 26 | } 27 | 28 | // PowerLess select titles of Power

-1 { 56 | c++ // starts at 1 57 | } 58 | return c 59 | } 60 | 61 | // GetAllMdTitles extracts all MD titles markup. 62 | func GetAllMdTitles(content string) []MdTitle { 63 | ret := []MdTitle{} 64 | allTitles := []string{} 65 | line := "" 66 | isInBlock := false 67 | isInTitle := false 68 | i := 0 69 | for _, c := range content { 70 | if !isInBlock && c == '\n' { 71 | if isInTitle { 72 | if mdTitle.MatchString(line) { 73 | got := mdTitle.FindAllStringSubmatch(line, -1) 74 | if len(got) > 0 { 75 | t := got[0][2] 76 | ret = append(ret, MdTitle{ 77 | Line: i, Title: t, 78 | Power: len(got[0][1]), 79 | Duplicate: cntStr(allTitles, t), 80 | }) 81 | allTitles = append(allTitles, t) 82 | } 83 | } 84 | } 85 | i++ 86 | isInTitle = false 87 | line = "" 88 | } else if c == '`' { 89 | isInBlock = !isInBlock 90 | line += string(c) 91 | } else if c == '#' && !isInBlock { 92 | isInTitle = true 93 | line += string(c) 94 | } else { 95 | if c == '\n' { 96 | i++ 97 | } 98 | line += string(c) 99 | } 100 | } 101 | return ret 102 | } 103 | 104 | // MakeTitleTree transform a raw list of titles into a tree. 105 | func MakeTitleTree(titles []MdTitle) *MdTitleTree { 106 | 107 | root := &MdTitleTree{} 108 | cur := root 109 | for _, t := range titles { 110 | 111 | if t.Power == 1 { 112 | nnew := &MdTitleTree{MdTitle: t, Parent: root} 113 | root.Items = append(root.Items, nnew) 114 | cur = nnew 115 | 116 | } else if t.Power > cur.Power { 117 | nnew := &MdTitleTree{MdTitle: t, Parent: cur} 118 | cur.Items = append(cur.Items, nnew) 119 | cur = nnew 120 | 121 | } else if t.Power == cur.Power { 122 | nnew := &MdTitleTree{MdTitle: t, Parent: cur.Parent} 123 | cur.Parent.Items = append(cur.Parent.Items, nnew) 124 | cur = nnew 125 | 126 | } else if t.Power < cur.Power { 127 | for { 128 | if cur.Parent.Power <= t.Power { 129 | break 130 | } 131 | cur = cur.Parent 132 | } 133 | cur = cur.Parent 134 | nnew := &MdTitleTree{MdTitle: t, Parent: cur.Parent} 135 | cur.Parent.Items = append(cur.Parent.Items, nnew) 136 | cur = nnew 137 | } 138 | } 139 | return root 140 | } 141 | 142 | // GetMdLinkHash encodes s to insert into an MD link. 143 | func GetMdLinkHash(link string) string { 144 | link = strings.ToLower(link) 145 | link = strings.Replace(link, "/", "", -1) 146 | link = strings.Replace(link, "$", "", -1) 147 | link = strings.Replace(link, ">", "", -1) 148 | link = strings.Replace(link, ".", "", -1) 149 | link = strings.Replace(link, ";", "", -1) 150 | link = strings.Replace(link, ":", "", -1) 151 | link = strings.Replace(link, "!", "", -1) 152 | link = strings.Replace(link, "'", "", -1) 153 | link = strings.Replace(link, "|", "", -1) 154 | link = strings.Replace(link, "[", "", -1) 155 | link = strings.Replace(link, "]", "", -1) 156 | link = strings.Replace(link, ",", "", -1) 157 | link = strings.Replace(link, " ", "-", -1) 158 | // should it be a regexp like /[a-z0-9-_]/i ? 159 | return link 160 | } 161 | 162 | // MdTitleTree is an MdTitle with tree capabilities 163 | type MdTitleTree struct { 164 | MdTitle 165 | Parent *MdTitleTree 166 | Items []*MdTitleTree 167 | } 168 | 169 | // Traverse a tree 170 | func (m *MdTitleTree) Traverse(f func(*MdTitleTree)) { 171 | f(m) 172 | for _, i := range m.Items { 173 | i.Traverse(f) 174 | } 175 | } 176 | 177 | func (m *MdTitleTree) String() string { 178 | x := strings.Repeat("#", m.Power) 179 | return fmt.Sprintf("%-5v %-15q Items:%v Line:%v", x, m.Title, len(m.Items), m.Line) 180 | } 181 | 182 | // MdTitle is a markdwon title. 183 | type MdTitle struct { 184 | Line int 185 | Power int 186 | Duplicate int 187 | Title string 188 | } 189 | -------------------------------------------------------------------------------- /utils/md_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | func TestLineIndex(t *testing.T) { 10 | content := `xx 11 | yyy 12 | search 13 | zzz 14 | ` 15 | want := 2 16 | got := LineIndex(content, "search") 17 | if want != got { 18 | t.Errorf("LineIndex fail, want=%v, got=%v", want, got) 19 | } 20 | } 21 | 22 | func TestGetAllMdTitles(t *testing.T) { 23 | content := `# one 24 | yyy 25 | ## two 26 | # three 27 | zzz 28 | ` 29 | 30 | got := GetAllMdTitles(content) 31 | iwant := 3 32 | igot := len(got) 33 | if iwant != igot { 34 | t.Errorf("GetAllMdTitles fail, len() want=%v, got=%v", iwant, igot) 35 | } 36 | 37 | sgot := got[0].Title 38 | swant := "one" 39 | if swant != sgot { 40 | t.Errorf("GetAllMdTitles fail, [0].Title want=%v, got=%v", swant, sgot) 41 | } 42 | iwant = 1 43 | igot = got[0].Power 44 | if iwant != igot { 45 | t.Errorf("GetAllMdTitles fail, [0].Power want=%v, got=%v", iwant, igot) 46 | } 47 | 48 | sgot = got[1].Title 49 | swant = "two" 50 | if swant != sgot { 51 | t.Errorf("GetAllMdTitles fail, [1].Title want=%v, got=%v", swant, sgot) 52 | } 53 | iwant = 2 54 | igot = got[1].Power 55 | if iwant != igot { 56 | t.Errorf("GetAllMdTitles fail, [0].Power want=%v, got=%v", iwant, igot) 57 | } 58 | 59 | sgot = got[2].Title 60 | swant = "three" 61 | if swant != sgot { 62 | t.Errorf("GetAllMdTitles fail, [1].Title want=%v, got=%v", swant, sgot) 63 | } 64 | iwant = 1 65 | igot = got[2].Power 66 | if iwant != igot { 67 | t.Errorf("GetAllMdTitles fail, [0].Power want=%v, got=%v", iwant, igot) 68 | } 69 | } 70 | 71 | func TestMakeTitleTree(t *testing.T) { 72 | content := `# one 73 | ## two 74 | # three 75 | # four 76 | ## four 1 77 | ### four 1-1 78 | ### four 1-2 79 | ## four 2 80 | ### four 2-1 81 | ### four 2-2 82 | # five 83 | ###### five 1-1 84 | ` 85 | 86 | titles := GetAllMdTitles(content) 87 | root := MakeTitleTree(titles) 88 | got := root.Items 89 | 90 | // fmt.Println(got[0]) 91 | // fmt.Println(got[1]) 92 | // fmt.Println(got[2]) 93 | // fmt.Println(got[2].Items[0]) 94 | // fmt.Println(got[2].Items[0].Items[0]) 95 | // fmt.Println(got[2].Items[0].Items[1]) 96 | // fmt.Println(got[2].Items[1]) 97 | // fmt.Println(got[3]) 98 | 99 | iwant := 4 100 | igot := len(got) 101 | if iwant != igot { 102 | t.Errorf("MakeTitleTree fail, len() want=%v, got=%v", iwant, igot) 103 | } 104 | 105 | swant := "one" 106 | sgot := got[0].Title 107 | if swant != sgot { 108 | t.Errorf("MakeTitleTree fail, [0].Title want=%v, got=%v", swant, sgot) 109 | } 110 | 111 | iwant = 1 112 | igot = got[0].Power 113 | if iwant != igot { 114 | t.Errorf("MakeTitleTree fail, [0].Power want=%v, got=%v", iwant, igot) 115 | } 116 | 117 | iwant = 1 118 | igot = len(got[0].Items) 119 | if iwant != igot { 120 | t.Errorf("MakeTitleTree fail, len([0].Items) want=%v, got=%v", iwant, igot) 121 | } 122 | 123 | swant = "two" 124 | sgot = got[0].Items[0].Title 125 | if swant != sgot { 126 | t.Errorf("MakeTitleTree fail, [0]Items[0].Title want=%v, got=%v", swant, sgot) 127 | } 128 | 129 | iwant = 2 130 | igot = got[0].Items[0].Power 131 | if iwant != igot { 132 | t.Errorf("MakeTitleTree fail, [0]Items[0].Power want=%v, got=%v", iwant, igot) 133 | } 134 | 135 | iwant = 1 136 | igot = got[1].Power 137 | if iwant != igot { 138 | t.Errorf("MakeTitleTree fail, [1].Power want=%v, got=%v", iwant, igot) 139 | } 140 | 141 | swant = "three" 142 | sgot = got[1].Title 143 | if swant != sgot { 144 | t.Errorf("MakeTitleTree fail, [0].Title want=%v, got=%v", swant, sgot) 145 | } 146 | 147 | iwant = 0 148 | igot = len(got[1].Items) 149 | if iwant != igot { 150 | t.Errorf("MakeTitleTree fail, len([1].Items) want=%v, got=%v", iwant, igot) 151 | } 152 | } 153 | 154 | func TestTraverse(t *testing.T) { 155 | content := `# one 156 | ## two 157 | # three 158 | # four 159 | ## four 1 160 | ### four 1-1 161 | ### four 1-2 162 | ## four 2 163 | ### four 2-1 164 | #### four 2-2 165 | # five 166 | ###### five 1-1 167 | ` 168 | 169 | titles := GetAllMdTitles(content) 170 | root := MakeTitleTree(titles) 171 | got := "" 172 | root.Traverse(PowerLess(5, func(n *MdTitleTree) { 173 | // got += MakeTOCItem(" ", n) + "\n" 174 | link := GetMdLinkHash(n.Title) 175 | x := strings.Repeat(" ", n.Power) 176 | got += fmt.Sprintf("%v- [%v](#%v)\n", x, n.Title, link) 177 | })) 178 | want := ` - [one](#one) 179 | - [two](#two) 180 | - [three](#three) 181 | - [four](#four) 182 | - [four 1](#four-1) 183 | - [four 1-1](#four-1-1) 184 | - [four 1-2](#four-1-2) 185 | - [four 2](#four-2) 186 | - [four 2-1](#four-2-1) 187 | - [four 2-2](#four-2-2) 188 | - [five](#five) 189 | ` 190 | if want != got { 191 | t.Errorf("TestTraverse failed, want=\n%v\ngot\n%v", want, got) 192 | } 193 | } 194 | 195 | func TestTraverse2(t *testing.T) { 196 | content := ` 197 | # Install 198 | 199 | ## go 200 | 201 | ` + "```" + `sh 202 | go get github.com/semver/cmd 203 | ` + "```" + ` 204 | 205 | # Cli 206 | 207 | ## Help 208 | 209 | #### $ go run main.go -help 210 | ` + "```" + `sh 211 | semver - 0.0.0 212 | 213 | Usage 214 | 215 | -filter|-c string Filter versions matching given semver constraint 216 | -invalid bool Show only invalid versions 217 | 218 | -sort|-s bool Sort input versions 219 | -desc|-d bool Sort versions descending 220 | 221 | -first|-f bool Only first version 222 | -last|-l bool Only last version 223 | 224 | -json|-j bool JSON output 225 | 226 | -version bool Show version 227 | 228 | Example 229 | 230 | semver -c 1.x 0.0.4 1.2.3 231 | exho "0.0.4 1.2.3" | semver -j 232 | exho "0.0.4 1.2.3" | semver -s 233 | exho "0.0.4 1.2.3" | semver -s -d -j -f 234 | exho "0.0.4 1.2.3 tomate" | semver -invalid 235 | ` + "```" + ` 236 | 237 | # Example 238 | 239 | ## Filter versions 240 | 241 | #### $ go run main.go -c 1.x 1.0.4 1.1.1 1.2.2 2.3.4 242 | ` + "```" + `sh 243 | - 1.0.4 244 | - 1.1.1 245 | - 1.2.2 246 | ` + "```" + ` 247 | 248 | ## Use stdin 249 | 250 | #### $ echo '1.0.4 1.1.1 1.2.2 2.3.4' | go run main.go -c 2.x 251 | ` + "```" + `sh 252 | - 2.3.4 253 | ` + "```" + ` 254 | 255 | ` 256 | 257 | titles := GetAllMdTitles(content) 258 | root := MakeTitleTree(titles) 259 | got := "" 260 | root.Traverse(PowerLess(5, func(n *MdTitleTree) { 261 | // got += MakeTOCItem(" ", n) + "\n" 262 | link := GetMdLinkHash(n.Title) 263 | x := strings.Repeat(" ", n.Power) 264 | got += fmt.Sprintf("%v- [%v](#%v)\n", x, n.Title, link) 265 | })) 266 | want := ` - [Install](#install) 267 | - [go](#go) 268 | - [Cli](#cli) 269 | - [Help](#help) 270 | - [$ go run main.go -help](#-go-run-maingo--help) 271 | - [Example](#example) 272 | - [Filter versions](#filter-versions) 273 | - [$ go run main.go -c 1.x 1.0.4 1.1.1 1.2.2 2.3.4](#-go-run-maingo--c-1x-104-111-122-234) 274 | - [Use stdin](#use-stdin) 275 | - [$ echo '1.0.4 1.1.1 1.2.2 2.3.4' | go run main.go -c 2.x](#-echo-104-111-122-234--go-run-maingo--c-2x) 276 | ` 277 | if want != got { 278 | t.Errorf("TestTraverse failed, want=\n%q\ngot\n%q", want, got) 279 | } 280 | } 281 | 282 | func TestGetMdLinkHash(t *testing.T) { 283 | sgot := GetMdLinkHash("/$ .>;:") 284 | swant := "-" 285 | if swant != sgot { 286 | t.Errorf("GetMdLinkHash fail, want=%v, got=%v", swant, sgot) 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /utils/prelude.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // GetPrelude decodes and remove the prelude from the input string. 10 | func GetPrelude(s string) (string, map[string]interface{}, error) { 11 | 12 | var v map[string]interface{} 13 | k := strings.Split(string(s), "\n") 14 | i := 0 15 | prelude := "" 16 | if len(k) > 1 && k[0] == "---" { 17 | i++ 18 | for _, l := range k[1:] { 19 | i++ 20 | if l == "---" { 21 | break 22 | } 23 | z := strings.Index(l, ":") 24 | name := l[0:z] 25 | val := l[z+1:] 26 | val = strings.TrimLeft(val, " ") 27 | // quote raw values, it allows easier writing of strings containing strings. 28 | if val[0] != '[' && val[0] != '"' && val[0] != '\'' { 29 | val = fmt.Sprintf("%q", val) 30 | } 31 | prelude += fmt.Sprintf("%q:%v,\n", name, val) 32 | } 33 | 34 | prelude = "{" + prelude[:len(prelude)-2] + "}" 35 | 36 | if err := json.Unmarshal([]byte(prelude), &v); err != nil { 37 | return "", v, err 38 | } 39 | } 40 | 41 | // rebuild the template without prelude 42 | c := "" 43 | for _, l := range k[i:] { 44 | c += fmt.Sprintf("%v\n", l) 45 | } 46 | 47 | return strings.TrimRight(c, "\n") + "\n", v, nil 48 | } 49 | -------------------------------------------------------------------------------- /utils/shellexec.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "os/exec" 7 | "path/filepath" 8 | "runtime" 9 | ) 10 | 11 | // Command Return a new exec.Cmd object for the given command string 12 | func Command(cwd string, cmd string) (*TempCmd, error) { 13 | return NewTempCmd(cwd, cmd) 14 | } 15 | 16 | // TempCmd ... 17 | type TempCmd struct { 18 | *exec.Cmd 19 | f string 20 | } 21 | 22 | var isWindows = runtime.GOOS == "windows" 23 | 24 | // NewTempCmd is a cmd wrapped into a tmp file 25 | func NewTempCmd(cwd string, cmd string) (*TempCmd, error) { 26 | f, err := ioutil.TempDir("", "stringexec") 27 | if err != nil { 28 | return nil, err 29 | } 30 | fp := filepath.Join(f, "s") 31 | if isWindows { 32 | fp += ".bat" 33 | } 34 | err = ioutil.WriteFile(fp, []byte(cmd), 0766) 35 | if err != nil { 36 | return nil, err 37 | } 38 | ret := &TempCmd{Cmd: exec.Command("sh", "-c", fp), f: fp} 39 | if isWindows { 40 | ret.Cmd = exec.Command("cmd", "/C", fp) 41 | } 42 | ret.Cmd.Dir = cwd 43 | return ret, nil 44 | } 45 | 46 | // Run the cmd 47 | func (t *TempCmd) Run() error { 48 | if err := t.Cmd.Start(); err != nil { 49 | return err 50 | } 51 | return t.Wait() 52 | } 53 | 54 | // Wait wait for command then delete the tmp file. 55 | func (t *TempCmd) Wait() error { 56 | err := t.Cmd.Wait() 57 | os.Remove(t.f) 58 | return err 59 | } 60 | 61 | // Shell exec a string. 62 | func Shell(wd, s string) (string, error) { 63 | if wd == "" { 64 | var err error 65 | wd, err = os.Getwd() 66 | if err != nil { 67 | return "", err 68 | } 69 | } 70 | cmd, err := NewTempCmd(wd, s) 71 | if err != nil { 72 | return "", &CliError{Err: err, Cmd: s} 73 | } 74 | out, err := cmd.CombinedOutput() 75 | if err != nil { 76 | return "", &CliError{Err: err, Cmd: s} 77 | } 78 | return string(out), nil 79 | } 80 | -------------------------------------------------------------------------------- /wix.json: -------------------------------------------------------------------------------- 1 | { 2 | "product": "emd", 3 | "company": "mh-cbon", 4 | "license": "LICENSE", 5 | "upgrade-code": "14F6E9E0-470C-46D0-80BF-2408266E37CB", 6 | "files": { 7 | "guid": "C15B46B7-FD6E-4DB9-9194-EFA544DEBB4B", 8 | "items": [ 9 | "emd.exe" 10 | ] 11 | }, 12 | "env": { 13 | "guid": "658A7B52-AC3D-466F-8B27-5E35FFC4FD4A", 14 | "vars": [ 15 | { 16 | "name": "PATH", 17 | "value": "[INSTALLDIR]", 18 | "permanent": "no", 19 | "system": "no", 20 | "action": "set", 21 | "part": "last" 22 | } 23 | ] 24 | }, 25 | "shortcuts": {}, 26 | "choco": { 27 | "description": "Enhanced Markdown template processor", 28 | "project-url": "https://github.com/mh-cbon/emd", 29 | "tags": "emd", 30 | "license-url": "https://github.com/mh-cbon/emd/blob/master/LICENSE" 31 | } 32 | } --------------------------------------------------------------------------------