Transaction

7FA3FBE10E5AB7…AA7C8F7C98D1

Block 406,149 · index 0 · indexed

Summary

Hash
7FA3FBE10E5AB77E3246F9D52E6868BDF71BDDDCF1A68FB764D0AA7C8F7C98D1
Block
406,149
Size
4342 bytes
Gas used
6,109,139 / 8,570,500
Fee
85705ugnot
Status
success

Messages

#1Call Setgno.land/r/moul/blog5 arguments
Attached funds
—
Function
Set

Arguments · 5

  1. #1gnopm
  2. #2sharing gnopm
  3. #32026-09-28
  4. #4gnopm,tooling,packages
  5. #5gnopm is a package manager I wrote because bumping a version in gno destroyed the diff. It keeps a package's version in its `gnomod.toml` instead of in its directory name, and everything else it does falls out of that. ## the diff that started it A gno import path ends in its version, so the obvious layout gives each version its own directory, and a bump means copying that directory. **git cannot pair a copy.** The review diff becomes added files with no content diff at all, which is backwards: a bump is by definition the compatibility change that most needs reviewing, and it is the one change you cannot see. `log --follow`, `blame` and `bisect` stop there too. One port of 25 realms landed in my repo as **+11,103 / -0 across 112 files**, every behaviour change invisible. The toolchain already reads the version from the module line, so the directory does not have to repeat it: ``` p/alice/md/gnomod.toml module = "gno.land/p/alice/md/v1" p/alice/md/md.gno edited in place ``` A bump is one line, then the real diff. The superseded version gets pinned in a `gnomod.lock` to the commit that still holds it and rebuilt on demand, so anything importing `.../md/v0` keeps resolving with no directory left. That lock is source, not a build artefact. A change that bumps a version carries the pin keeping the old one resolvable, so it is the author who commits it and CI that proves it still reproduces. ## a version number is a tag, not a counter This one cost me before I understood it. A number means nothing until the version is published: until then it names no bytes and derives no address. So you bump when the last version shipped, and edit in place when it did not. Getting it wrong is the *default* behaviour of a stack of pull requests. The first bumps v0 to v1 and lands. The second is written against it, sees v1 taken, goes to v2. Both merge before either is published, and the package reaches a chain as v2 with v1 existing nowhere. I did it to four packages in a single stacked pair of pull requests, and the lock could not show me: a skipped number is never recorded, so it jumps v0 to v2 with nothing in between. `gnopm tidy` asks the chain, which is the only thing that knows, and names the `unbump` that folds it back. That was an 8-line diff of module lines once I could see it. ## 325 packages My own repo was never the real test. `examples/` in the gno monorepo is: thousands of files, many versions, a layout nobody designed with this in mind. Ran it on 2026-09-28 against master, in a throwaway worktree. 325 packages, 97 lifted out of versioned directories, **987 renames and zero content changes in 19.9 seconds**. `gnopm verify` over the result: 0.08s, all 325 ok. `gno lint` and `gno test` pass on the migrated packages, using gno's own binary. One package blocked it, correctly: a quarantined realm that exists both unversioned and under `v1/` and `v2/`, so lifting v2 has nowhere to land and nothing can say which is current. I have not opened that as a pull request and would not from a script. It is 994 files in somebody else's repo, and which layout a project wants is a maintainer's call. The rehearsal just means that conversation can start with measurements instead of a claim. ## please break it [moul/gnopm](https://github.com/moul/gnopm). One Go binary, no third-party dependencies, no gno toolchain needed to build it. ```sh go install moul.io/gnopm@latest gnopm init ``` If you arrive from npm and type `gnopm install`, it tells you why there is nothing to install rather than "unknown command". There is no resolver and never will be: an import path carries its version, so two versions are two paths and there is no constraint to solve. gno skipped the entire class of problem that produces SAT solvers, by accident of path design, and I am not reintroducing it. What I want is an issue when it surprises you, and that counts when it behaved exactly as documented. Most of what is in there started as somebody, usually me, saying "I expected it to just do that".

Result log

msg:0,success:true,log:,events:[]

← Back to block 406,149