github.com/whyrusleeping/gx@v0.14.3/README.md (about) 1 ![gx logo](logo.jpeg) 2 3 # gx 4 > The language-agnostic, universal package manager 5 6 [![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai) [![](https://img.shields.io/badge/freenode-%23gx-blue.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23ipfs,%23gx) 7 8 gx is a packaging tool built around the distributed, content addressed filesystem 9 [IPFS](//github.com/ipfs/ipfs). It aims to be flexible, powerful and simple. 10 11 gx is **Alpha Quality**. While not perfect, gx is reliable enough 12 to manage dependencies in [go-ipfs](https://github.com/ipfs/go-ipfs/) and 13 is ready for use by developers of all skill levels. 14 15 ## Table of Contents 16 - [Background](#background) 17 - [Requirements](#requirements) 18 - [Installation](#installation) 19 - [Usage](#usage) 20 - [Dependencies](#dependencies) 21 - [Updating](#updating) 22 - [Repos](#repos) 23 - [Usage](#usage-1) 24 - [Hooks](#hooks) 25 - [The vendor directory](#the-vendor-directory) 26 - [Using gx as a Go package manager](#using-gx-as-a-go-package-manager) 27 - [Using gx as a Javascript package manager](#using-gx-as-a-javascrit-package-manager) 28 - [Using gx as a package manager for language/environment X](#using-gx-as-a-package-manager-for-languageenvironment-x) 29 - [Why is it called gx?](#why-is-it-called-gx) 30 - [License](#license) 31 32 ## Background 33 34 gx was originally designed to handle dependencies in Go projects in a 35 distributed fashion, and pulls ideas from other beloved package managers (like 36 [npm](http://npmjs.org/)). 37 38 gx was designed with the following major goals in mind: 39 40 1. Be language/ecosystem agnostic by providing [git-like hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) for adding [new ecosystems](https://github.com/whyrusleeping/gx-go). 41 2. Provide completely reproducible packages through content addressing. 42 3. Use [a flexible, distributed storage backend](http://ipfs.io/). 43 44 45 ## Requirements 46 Users are encouraged to have a running [IPFS daemon](//github.com/ipfs/go-ipfs) of at least version 0.4.2 on their machines. 47 If not present, gx will use the public gateway. 48 If you wish to publish a package, a local running daemon is a hard requirement. If your IPFS repo is in a non-standard location, remember to set $IPFS_PATH. Alternatively, you can explicitly set $IPFS_API to $IPFS_API_IPADDR:$PORT. 49 50 51 ## Installation 52 53 ``` 54 $ (cd ~ && go get github.com/whyrusleeping/gx) 55 ``` 56 57 This will download, build, and install a binary to `$GOPATH/bin`. To modify gx, 58 just change the source in that directory, and run `go build`. 59 60 ## Usage 61 62 Creating and publishing new generic package: 63 64 ```bash 65 $ gx init 66 $ gx publish 67 ``` 68 69 This will output a 'package-hash' unique to the content of the 70 package at the time of publishing. If someone downloads the package and 71 republishes it, the *exact* same hash will be produced. 72 73 ### package.json 74 75 It should be noted that gx is meant to *work with* existing `package.json` files. If you are adding a package to gx that already has a `package.json` file in its root, gx will try and work with it. Any shared fields will have the same types, and any fields unique to gx will kept separate. 76 77 E.g. A single `package.json` file could be used to serve both gx and another packaging tool, such as npm. Since gx is **Alpha Quality** there may be some exceptions to the above statements, if you notice one, please file an issue. 78 79 ## Installing a gx package 80 If you've cloned down a gx package, simply run `gx install` or `gx i` to 81 install it (and its dependencies). 82 83 ## Dependencies 84 To add a dependency of another package to your package, simply import it by its 85 hash: 86 87 ```bash 88 $ gx import QmaDFJvcHAnxpnMwcEh6VStYN4v4PB4S16j4pAuC2KSHVr 89 ``` 90 91 This downloads the package specified by the hash into the `vendor` directory in your 92 workspace. It also adds an entry referencing the package to the local `package.json`. 93 94 Gx has a few nice tools to view and analyze dependencies. First off, the simple: 95 96 ```bash 97 $ gx deps 98 go-log QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52 1.2.0 99 go-libp2p-peer QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p 2.0.4 100 go-libp2p-peerstore QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD 1.2.5 101 go-testutil QmYpVUnnedgGrp6cX2pBii5HRQgcSr778FiKVe7o7nF5Z3 1.0.2 102 go-ipfs-util QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1 1.0.0 103 ``` 104 105 This just lists out the immediate dependencies of this package. To see 106 dependencies of dependencies, use the `-r` option: (and optionally the `-s` 107 option to sort them) 108 109 ```bash 110 $ gx deps -r -s 111 go-base58 QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf 0.0.0 112 go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 113 go-datastore QmbzuUusHqaLLoNTDEVLcSF6vZDHZDLPC7p4bztRvvkXxU 1.0.0 114 go-ipfs-util QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1 1.0.0 115 go-keyspace QmUusaX99BZoELh7dmPgirqRQ1FAmMnmnBn3oiqDFGBUSc 1.0.0 116 go-libp2p-crypto QmVoi5es8D5fNHZDqoW6DgDAEPEV5hQp8GBz161vZXiwpQ 1.0.4 117 go-libp2p-peer QmWXjJo15p4pzT7cayEwZi2sWgJqLnGDof6ZGMh9xBgU1p 2.0.4 118 go-libp2p-peerstore QmYkwVGkwoPbMVQEbf6LonZg4SsCxGP3H7PBEtdNCNRyxD 1.2.5 119 go-log QmSpJByNKFX1sCsHBEp3R73FL4NF6FnQTEGyNAXHm2GS52 1.2.0 120 go-logging QmQvJiADDe7JR4m968MwXobTCCzUqQkP87aRHe29MEBGHV 0.0.0 121 go-multiaddr QmYzDkkgAEmrcNzFCiYo6L1dTX4EAG1gZkbtdbd9trL4vd 0.0.0 122 go-multiaddr-net QmY83KqqnQ286ZWbV2x7ixpeemH3cBpk8R54egS619WYff 1.3.0 123 go-multihash QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku 0.0.0 124 go-net QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt 0.0.0 125 go-testutil QmYpVUnnedgGrp6cX2pBii5HRQgcSr778FiKVe7o7nF5Z3 1.0.2 126 go-text Qmaau1d1WjnQdTYfRYfFVsCS97cgD8ATyrKuNoEfexL7JZ 0.0.0 127 go.uuid QmcyaFHbyiZfoX5GTpcqqCPYmbjYNAhRDekXSJPFHdYNSV 1.0.0 128 gogo-protobuf QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV 0.0.0 129 goprocess QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP 1.0.0 130 mafmt QmeLQ13LftT9XhNn22piZc3GP56fGqhijuL5Y8KdUaRn1g 1.1.1 131 ``` 132 133 That's pretty useful, I now know the full set of packages my package depends on. 134 But what's difficult now is being able to tell what is imported where. To 135 address that, gx has a `--tree` option: 136 137 ```bash 138 $ gx deps --tree 139 ├─ go-base58 QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf 0.0.0 140 ├─ go-multihash QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku 0.0.0 141 │ ├─ go-base58 QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf 0.0.0 142 │ └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 143 ├─ go-ipfs-util QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1 1.0.0 144 │ ├─ go-base58 QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf 0.0.0 145 │ └─ go-multihash QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku 0.0.0 146 │ ├─ go-base58 QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf 0.0.0 147 │ └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 148 ├─ go-log QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR 1.1.2 149 │ ├─ randbo QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T 0.0.0 150 │ ├─ go-net QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt 0.0.0 151 │ │ ├─ go-text Qmaau1d1WjnQdTYfRYfFVsCS97cgD8ATyrKuNoEfexL7JZ 0.0.0 152 │ │ └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 153 │ └─ go-logging QmQvJiADDe7JR4m968MwXobTCCzUqQkP87aRHe29MEBGHV 0.0.0 154 └─ go-libp2p-crypto QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY 1.0.2 155 ├─ gogo-protobuf QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV 0.0.0 156 ├─ go-log Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH 0.0.0 157 │ ├─ go.uuid QmPC2dW6jyNzzBKYuHLBhxzfWaUSkyC9qaGMz7ciytRSFM 0.0.0 158 │ ├─ go-logging QmQvJiADDe7JR4m968MwXobTCCzUqQkP87aRHe29MEBGHV 0.0.0 159 │ ├─ go-net QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt 0.0.0 160 │ │ ├─ go-text Qmaau1d1WjnQdTYfRYfFVsCS97cgD8ATyrKuNoEfexL7JZ 0.0.0 161 │ │ └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 162 │ └─ randbo QmYvsG72GsfLgUeSojXArjnU6L4Wmwk7wuAxtNLuyXcc1T 0.0.0 163 ├─ go-ipfs-util QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1 1.0.0 164 │ ├─ go-base58 QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf 0.0.0 165 │ └─ go-multihash QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku 0.0.0 166 │ ├─ go-base58 QmT8rehPR3F6bmwL6zjUN8XpiDBFFpMP2myPdC6ApsWfJf 0.0.0 167 │ └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 168 └─ go-msgio QmRQhVisS8dmPbjBUthVkenn81pBxrx1GxE281csJhm2vL 0.0.0 169 └─ go-randbuf QmYNGtJHgaGZkpzq8yG6Wxqm6EQTKqgpBfnyyGBKbZeDUi 0.0.0 170 ``` 171 172 Now you can see the *entire* tree of dependencies for this project. Although, 173 for larger projects, this will get messy. If you're just interested in the 174 dependency tree of a single package, you can use the `--highlight` option 175 to filter the trees printing: 176 177 ```bash 178 $ gx deps --tree --highlight=go-crypto 179 ├─ go-multihash QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku 0.0.0 180 │ └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 181 ├─ go-ipfs-util QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1 1.0.0 182 │ └─ go-multihash QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku 0.0.0 183 │ └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 184 ├─ go-log QmNQynaz7qfriSUJkiEZUrm2Wen1u3Kj9goZzWtrPyu7XR 1.1.2 185 │ └─ go-net QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt 0.0.0 186 │ └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 187 └─ go-libp2p-crypto QmUEUu1CM8bxBJxc3ZLojAi8evhTr4byQogWstABet79oY 1.0.2 188 ├─ go-log Qmazh5oNUVsDZTs2g59rq8aYQqwpss8tcUWQzor5sCCEuH 0.0.0 189 │ └─ go-net QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt 0.0.0 190 │ └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 191 └─ go-ipfs-util QmZNVWh8LLjAavuQ2JXuFmuYH3C11xo988vSgp7UQrTRj1 1.0.0 192 └─ go-multihash QmYf7ng2hG5XBtJA3tN34DQ2GUN5HNksEw1rLDkmr6vGku 0.0.0 193 └─ go-crypto Qme1boxspcQWR8FBzMxeppqug2fYgYc15diNWmqgDVnvn2 0.0.0 194 ``` 195 196 This tree is a subset of the previous one, filtered to only show leaves that 197 end in the selected package. 198 199 The gx deps command also has two other smaller subcommands, `dupes` and 200 `stats`. `gx deps dupes` will print out packages that are imported multiple 201 times with the same name, but different hashes. This is useful to see if 202 different versions of the same package have been imported in different places 203 in the dependency tree. Allowing the user to more easily address the 204 discrepancy. `gx deps stats` will output the total number of packages imported 205 (total and unique) as well as the average depth of imports in the tree. This 206 gives you a rough idea of the complexity of your package. 207 208 ### The gx dependency graph manifesto 209 I firmly believe that packages are better when: 210 211 #### 1. The depth of the dependency tree is minimized. 212 This means restructuring your code in such a way that flattens (and perhaps 213 widens as a consequence) the tree. For example, in Go, this often times means 214 making an interface its own package, and implementations into their own 215 separate packages. The benefits here are that flatter trees are far easier to 216 update. For every package deep a dependency is, you have to update, test, 217 commit, review and merge another package. That's a lot of work, and also a lot 218 of extra room for problems to sneak in. 219 220 #### 2. The width of the tree is minimized, but not at the cost of increasing depth. 221 This should be fairly common sense, but striving to import packages only where 222 they are actually needed helps to improve code quality. Imagine having a helper 223 function in one package, simply because it's convenient to have it there, that 224 depends on a bunch of other imports from elsewhere in the tree. Sure it's nice, 225 and doesn't actually increase the 'total' number of packages you depend on. But 226 now you've created an extra batch of work for you to do any time any of these 227 are updated, and you also now force anyone who wants to import the package with 228 your helper function to also import all those other dependencies. 229 230 Adhering to the above two rules should (I'm very open to discussion on this) 231 improve overall code quality, and make your codebase far easier to navigate and 232 work on. 233 234 ## Updating 235 Updating packages in gx is simple: 236 237 ```bash 238 $ gx update mypkg QmbH7fpAV1FgMp6J7GZXUV6rj6Lck5tDix9JJGBSjFPgUd 239 ``` 240 241 This looks into your `package.json` for a dependency named `mypkg` and replaces 242 its hash reference with the one given. 243 244 Alternatively, you can just specify the hash you want to update to: 245 246 ```bash 247 $ gx update QmbH7fpAV1FgMp6J7GZXUV6rj6Lck5tDix9JJGBSjFPgUd 248 ``` 249 250 Doing it this way will pull down the package, check its name, and then update 251 that dependency. 252 253 Note that by default, this will not touch your code at all, so any references 254 to that hash you have in your code will need to be updated. If you have a 255 language tool (e.g. `gx-go`) installed, and it has a `post-update` hook, 256 references to the given package should be updated correctly. If not, you may 257 have to run sed over the package to update everything. The bright side of that 258 is that you are very unlikely to have those hashes sitting around for any other 259 reason so a global find-replace should be just fine. 260 261 ## Publishing and Releasing 262 Gx by default will not let you publish a package twice if you haven't updated 263 its version. To get around this, you can pass the `-f` flag. Though this is not 264 recommended, it's still perfectly possible to do. 265 266 To update the version easily, use the `gx version` subcommand. You can either set the version manually: 267 268 ```bash 269 $ gx version 5.11.4 270 ``` 271 272 Or just do a 'version bump': 273 274 ```bash 275 $ gx version patch 276 updated version to: 5.11.5 277 $ gx version minor 278 updated version to: 5.12.0 279 $ gx version major 280 updated version to: 6.0.0 281 ``` 282 283 Most of the time, your process will look something like: 284 285 ```bash 286 $ gx version minor 287 updated version to: 6.1.0 288 $ gx publish 289 package whys-awesome-package published with hash: QmaoaEi6uNMuuXKeYcXM3gGUEQLzbDWGcFUdd3y49crtZK 290 $ git commit -a -m "gx publish 6.1.0" 291 [master 5c4d36c] gx publish 6.1.0 292 2 files changed, 3 insertions(+), 2 deletions(-) 293 ``` 294 295 The `release` subcommand can be used to automate the above process. `gx release <version>` 296 will do a version update (using the same inputs as the normal 297 `version` command), run a `gx publish`, and then execute whatever you have set 298 in your `package.json` as your `releaseCmd`. To get the above git commit flow, 299 you can set it to: `git commit -a -m \"gx publish $VERSION\"` and gx will 300 replace `$VERSION` with the newly changed version before executing the git 301 commit. 302 303 ### Ignoring files from a publish 304 You can use a `.gxignore` file to make gx ignore certain files during a publish. 305 This has the same behaviour as a `.gitignore`. 306 307 Gx also respects a `.gitignore` file if present, and will not publish any file 308 excluded by it. 309 310 311 ## Repos 312 gx supports named packages via user configured repositories. A repository is 313 simply an ipfs object whose links name package hashes. You can add a repository 314 as either an ipns or ipfs path. 315 316 ### Usage 317 318 Add a new repo 319 ```bash 320 $ gx repo add myrepo /ipns/QmPupmUqXHBxikXxuptYECKaq8tpGNDSetx1Ed44irmew3 321 ``` 322 323 List configured repos 324 ```bash 325 $ gx repo list 326 myrepo /ipns/QmPupmUqXHBxikXxuptYECKaq8tpGNDSetx1Ed44irmew3 327 ``` 328 329 List packages in a given repo 330 ```bash 331 $ gx repo list myrepo 332 events QmeJjwRaGJfx7j6LkPLjyPfzcD2UHHkKehDPkmizqSpcHT 333 smalltree QmRgTZA6jGi49ipQxorkmC75d3pLe69N6MZBKfQaN6grGY 334 stump QmebiJS1saSNEPAfr9AWoExvpfGoEK4QCtdLKCK4z6Qw7U 335 ``` 336 337 Import a package from a repo: 338 ```bash 339 $ gx repo import events 340 ``` 341 342 ## Hooks 343 gx supports a wide array of use cases by having sane defaults that are 344 extensible based on the scenario the user is in. To this end, gx has hooks that 345 get called during certain operations. 346 347 These hooks are language specific, and gx will attempt to make calls to a 348 helper binary matching your language to execute the hooks. For example, when 349 writing go, gx calls `gx-go hook <hookname> <args>` for any given hook. 350 351 Currently available hooks are: 352 353 - `post-import` 354 - called after a new package is imported and its info written to package.json. 355 - takes the hash of the newly imported package as an argument. 356 - `post-init` 357 - called after a new package is initialized. 358 - takes an optional argument of the directory of the newly init'ed package. 359 - `pre-publish` 360 - called during `gx publish` before the package is bundled up and added to ipfs. 361 - currently takes no arguments. 362 - `post-publish` 363 - called during `gx publish` after the package has been added to ipfs. 364 - takes the hash of the newly published package as an argument. 365 - `post-update` 366 - called during `gx update` after a dependency has been updated. 367 - takes the old package ref and the new hash as arguments. 368 - `post-install` 369 - called after a new package is downloaded, during install and import. 370 - takes the path to the new package as an argument. 371 - `install-path` 372 - called during package installs and imports. 373 - sets the location for gx to install packages to. 374 375 ## Package directories 376 377 Gx by default will install packages 'globally' in the global install location 378 for your given project type. Global gx packages are shared across all packages 379 that depend on them. The location of this directory can be changed if desired. Add a hook 380 to your environments extension tool named `install-path` (see above) and gx 381 will use that path instead. If your language does not set a global install 382 path, gx will fallback to installing locally as the default. This means that 383 it will create a folder in the current directory named `vendor` and install 384 things to it. 385 386 When running `gx install` in the directory of your package, gx will recursively 387 fetch all of the dependencies specified in the `package.json` and save them to 388 the install path specified. 389 390 Gx supports both local and global installation paths. Since the default is 391 global, to install locally, use `--local` or `--global=false`. The global flag 392 is passed to the `install-path` hook for your extension code to use in its 393 logic. 394 395 ## Using gx as a Go package manager 396 397 If you want (like me) to use gx as a package manager for go, it's pretty easy. 398 You will need the gx go extensions before starting your project: 399 ``` 400 $ go get -u github.com/whyrusleeping/gx-go 401 ``` 402 403 Once that's installed, use gx like normal to import dependencies. 404 You can import code from the vendor directory using: 405 ```go 406 import "gx/ipfs/<hash>/packagename" 407 ``` 408 409 for example, if i have a package foobar, you can import with gx it like so: 410 ```bash 411 $ gx import QmR5FHS9TpLbL9oYY8ZDR3A7UWcHTBawU1FJ6pu9SvTcPa 412 ``` 413 414 And then in your go code, you can use it with: 415 ```go 416 import "gx/ipfs/QmR5FHS9TpLbL9oYY8ZDR3A7UWcHTBawU1FJ6pu9SvTcPa/foobar" 417 ``` 418 419 Then simply set the environment variable `GO15VENDOREXPERIMENT` to `1` and run 420 `go build` or `go install` like you normally would. Alternatively, install 421 your dependencies globally (`gx install --global`) and you can leave off the 422 environment variable part. 423 424 See [the gx-go repo](https://github.com/whyrusleeping/gx-go) for more details. 425 426 ## Using gx as a Javascript package manager 427 428 Please take a look at [gx-js](https://github.com/sterpe/gx-js). 429 430 ## Using gx as a package manager for language/environment X 431 432 If you want to use gx with a big bunch of repositories/packages please 433 take a look at [gx-workspace](https://github.com/ipfs/gx-workspace). 434 435 If you want to extend gx to work with any other language or environment, you 436 can implement the relevant hooks in a binary named `gx-X` where the 'X' is the 437 name of your environment. After that, any package whose language is set to 'X' 438 will call out to that tools hooks during normal `gx` operations. For example, a 439 'go' package would call `gx-go hook pre-publish` during a `gx publish` 440 invocation before the package is actually published. For more information on 441 hooks, check out the hooks section above. 442 443 See also the `examples` directory. 444 445 ## Why is it called gx? 446 447 No reason. "gx" stands for nothing. 448 449 ## Getting Involved 450 451 If you're interested in gx, please stop by #gx and #ipfs on freenode irc! 452 453 ## License 454 455 MIT. Jeromy Johnson.