├── .gitignore ├── LICENSE ├── README.md ├── docs └── nimbyLogo.png ├── nim.cfg ├── nimby.nimble └── src └── nimby.nim /.gitignore: -------------------------------------------------------------------------------- 1 | # ignore files with no extention: 2 | * 3 | !*/ 4 | !*.* 5 | 6 | # normal ignores: 7 | *.exe 8 | nimcache 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT 2 | 3 | Copyright 2018 Andre von Houck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Nimby Logo](docs/nimbyLogo.png) 2 | 3 | # Nimby 4 | 5 | `nimble install nimby` 6 | 7 | ![Github Actions](https://github.com/treeform/nimby/workflows/Github%20Actions/badge.svg) 8 | 9 | [API reference](https://treeform.github.io/nimby) 10 | 11 | This library has no dependencies other than the Nim standard library. 12 | 13 | ## About 14 | 15 | Nimby is a very simple tool to help with managing many Nim packages. If you have a ton of packages you are working on, this tool can help to keep everything up to date. And making sure readme, license and and nimble use a uniform style. Also helps you push and develop all of the at once. 16 | 17 | ``` 18 | nimby - manage a large collection of nimble packages in development 19 | - list list all Nim packages in the current directory 20 | - develop make sure all packages are linked with nimble 21 | - pull pull all updates to packages from with git 22 | - tag create a git tag for all pacakges if needed 23 | ``` 24 | -------------------------------------------------------------------------------- /docs/nimbyLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/treeform/nimby/5212b3a863f40c86e3e63d2090d72f11149ef7b5/docs/nimbyLogo.png -------------------------------------------------------------------------------- /nim.cfg: -------------------------------------------------------------------------------- 1 | --d:ssl 2 | -------------------------------------------------------------------------------- /nimby.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "Andre von Houck" 5 | description = "Nimby helps you manage many nim packages." 6 | license = "MIT" 7 | srcDir = "src" 8 | 9 | # Dependencies 10 | 11 | requires "nim >= 1.0.0" 12 | 13 | bin = @["nimby"] 14 | -------------------------------------------------------------------------------- /src/nimby.nim: -------------------------------------------------------------------------------- 1 | # Nimby helps manage a large collection of nimble packages in development. 2 | import os, osproc, parsecfg, parseopt, strutils, terminal, 3 | strutils, strformat, puppy, tables, strformat 4 | 5 | let minDir = getCurrentDir() 6 | 7 | proc cutBetween(str, a, b: string): string = 8 | let 9 | cutA = str.find(a) 10 | if cutA == -1: 11 | return "" 12 | let 13 | cutB = str.find(b, cutA) 14 | if cutB == -1: 15 | return "" 16 | return str[cutA + a.len.. 1: 144 | let remoteUrl = parseUrl(remoteArr[1]) 145 | let remoteLibName = remoteUrl.paths[0].rmSuffix(".git") 146 | if result != remoteLibName: 147 | error &"path {result}/ does not match git name {remoteLibName}" 148 | 149 | proc authorName(): string = 150 | let remoteArr = execProcess("git remote -v").split() 151 | if remoteArr.len > 1: 152 | let remoteUrl = parseUrl(remoteArr[1]) 153 | result = remoteUrl.port 154 | 155 | var authorRealNameCache: Table[string, string] 156 | proc authorRealName(): string = 157 | let name = authorName() 158 | if name notin authorRealNameCache: 159 | let 160 | githubUrl = "https://github.com/" & name 161 | res = fetch(githubUrl) 162 | realName = res.cutBetween("itemprop=\"name\">", "").strip() 163 | authorRealNameCache[name] = realName 164 | return authorRealNameCache[name] 165 | 166 | proc mostRecentVersion(libName: string): string = 167 | let nimblePath = &"../{libName}/{libName}.nimble" 168 | if fileExists(nimblePath): 169 | for line in readFile(nimblePath).split("\n"): 170 | if line.startsWith("version"): 171 | return line.splitWhitespace()[^1][1..^2] 172 | 173 | proc check() = 174 | ## Checks to see if readme/licnese/nimble are up to current standard 175 | 176 | if not validNimPackage(): 177 | return 178 | 179 | let lib = libName() 180 | 181 | var 182 | author = authorName() 183 | authorReal = authorRealName() 184 | echo "* ", lib, " by ", author, " (" & authorReal & ")" 185 | 186 | if not fileExists("LICENSE"): 187 | error &"No {lib}/LICENSE file! " 188 | return 189 | 190 | if not fileExists(lib & ".nimble"): 191 | error &"No {lib}/{lib}.nimble file! " 192 | return 193 | 194 | var 195 | readmeSec = readmeSection.replace("$lib", lib).replace("$author", author) 196 | nimble = readFile(lib & ".nimble") 197 | license = readFile("LICENSE") 198 | 199 | var libs: seq[string] 200 | for line in nimble.split("\n"): 201 | if line.startsWith("requires"): 202 | let lib = line.replace("requires \"", "").split(" ")[0] 203 | if lib != "nim": 204 | libs.add(lib) 205 | if line.startsWith("author") and authorRealName() notin line: 206 | error "nimble: " & line 207 | if "requires" in line: 208 | if ">=" notin line: 209 | error "nimble: " & line 210 | else: 211 | let 212 | arr = line.strip().split() 213 | libRequired = arr[1][1..^1] 214 | versionRequired = arr[3][0..^2] 215 | versionInstalled = mostRecentVersion(libRequired) 216 | if libRequired != "nim" and versionInstalled != "" and versionRequired != versionInstalled: 217 | error &"nimble update dep: {libRequired} {versionRequired} -> {versionInstalled}" 218 | 219 | for line in license.split("\n"): 220 | if line.startsWith("Copyright") and authorRealName() notin line: 221 | error "update to? " & authorRealName() 222 | error "LICENSE: " & line 223 | 224 | if libs.len == 0: 225 | readmeSec.add "This library has no dependencies other than the Nim standard library.\n\n" 226 | 227 | let readme = readFile("README.md") 228 | if "nimble install" in readme and readmeSec notin readme: 229 | error readmeSec 230 | 231 | if dirExists(".github/workflows"): 232 | # make sure build.yml and docs.yml same 233 | if not fileExists(".github/workflows/build.yml"): 234 | error "missing .github/workflows/build.yml" 235 | elif readFile(".github/workflows/build.yml") != buildYaml: 236 | error "different .github/workflows/build.yml" 237 | 238 | if not fileExists(".github/workflows/docs.yml"): 239 | error "missing .github/workflows/docs.yml" 240 | elif readFile(".github/workflows/docs.yml") != docsYaml: 241 | error "different .github/workflows/docs.yml" 242 | 243 | proc fixRemote() = 244 | var 245 | remoteArr = execProcess("git remote -v").split() 246 | if remoteArr.len > 1: 247 | let remoteUrl = remoteArr[1] 248 | if "https://github.com/" in remoteUrl: 249 | let gitUrl = remoteUrl.replace("https://github.com/", "git@github.com:") 250 | echo remoteUrl, " -> ", gitUrl 251 | cmd &"git remote remove origin" 252 | cmd &"git remote add origin {gitUrl}" 253 | cmd &"git pull origin master" 254 | 255 | proc fixCi() = 256 | if dirExists(".github/workflows"): 257 | # make sure build.yml is correct 258 | if not fileExists(".github/workflows/build.yml"): 259 | error "missing .github/workflows/build.yml" 260 | writeFile(".github/workflows/build.yml", buildYaml) 261 | elif readFile(".github/workflows/build.yml") != buildYaml: 262 | error "different .github/workflows/build.yml" 263 | writeFile(".github/workflows/build.yml", buildYaml) 264 | 265 | proc fixDocs() = 266 | if dirExists(".github/workflows"): 267 | # Add github actions doc builder. 268 | cmd &"git add -f .github/workflows/docs.yml" 269 | # make sure docs.yml is correct 270 | if not fileExists(".github/workflows/docs.yml"): 271 | error "missing .github/workflows/docs.yml" 272 | writeFile(".github/workflows/docs.yml", docsYaml) 273 | elif readFile(".github/workflows/docs.yml") != docsYaml: 274 | error "different .github/workflows/docs.yml" 275 | writeFile(".github/workflows/docs.yml", docsYaml) 276 | 277 | proc pull() = 278 | cmd "git pull" 279 | 280 | proc develop() = 281 | cmd "nimble develop -y" 282 | 283 | proc test() = 284 | cmd "nimble test" 285 | 286 | proc actionBoard() = 287 | if not validNimPackage(): 288 | return 289 | 290 | let lib = libName() 291 | 292 | var 293 | author = authorName() 294 | authorReal = authorRealName() 295 | 296 | echo "* ", lib, " by ", author, " (" & authorReal & ")" 297 | echo "![Github Actions](https://github.com/" & author & "/" & lib & "/workflows/Github%20Actions/badge.svg" 298 | 299 | 300 | proc walkAll(operation: proc()) = 301 | for dirKind, dir in walkDir("."): 302 | if dirKind != pcDir: 303 | continue 304 | setCurrentDir(minDir / dir) 305 | #echo "------ ", minDir / dir, " ------" 306 | operation() 307 | setCurrentDir(minDir) 308 | 309 | var subcommand, url: string 310 | var p = initOptParser() 311 | for kind, key, val in p.getopt(): 312 | case kind 313 | of cmdArgument: 314 | if subcommand == "": 315 | subcommand = key 316 | else: 317 | url = key 318 | of cmdLongOption, cmdShortOption: 319 | case key 320 | of "help", "h": writeHelp() 321 | of "version", "v": writeVersion() 322 | of cmdEnd: assert(false) # cannot happen 323 | 324 | case subcommand 325 | of "": writeHelp() 326 | of "list": walkAll(list) 327 | of "urls": walkAll(urls) 328 | of "commit": walkAll(commit) 329 | of "check": walkAll(check) 330 | of "fix-remote": walkAll(fixRemote) 331 | of "fix-ci": walkAll(fixCi) 332 | of "fix-docs": walkAll(fixDocs) 333 | of "develop": walkAll(develop) 334 | of "pull": walkAll(pull) 335 | of "test": walkAll(test) 336 | of "action-board": walkAll(actionBoard) 337 | else: 338 | echo "invalid command" 339 | --------------------------------------------------------------------------------