Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: initial 'gno lint' #981

Merged
merged 10 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,23 @@ jobs:
- uses: actions/checkout@v3
- run: go install -v ./gnovm/cmd/gno
- run: go run ./gnovm/cmd/gno test --verbose ./examples
lint:
strategy:
fail-fast: false
matrix:
go-version: [ "1.19.x", "1.20.x" ]
# unittests: TODO: matrix with contracts
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
- uses: actions/checkout@v3
- run: go install -v ./gnovm/cmd/gno
# testing official directories, basically examples/ minus examples/.../x/.
- run: go run ./gnovm/cmd/gno lint --verbose ./examples/gno.land/p
- run: go run ./gnovm/cmd/gno lint --verbose ./examples/gno.land/r/demo
- run: go run ./gnovm/cmd/gno lint --verbose ./examples/gno.land/r/gnoland
- run: go run ./gnovm/cmd/gno lint --verbose ./examples/gno.land/r/system
# TODO: track coverage
10 changes: 10 additions & 0 deletions examples/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
# Official packages: more reliable and tested modules, distinct from the experimentation area.
OFFICIAL_PACKAGES = ./gno.land/p
OFFICIAL_PACKAGES += ./gno.land/r/demo
OFFICIAL_PACKAGES += ./gno.land/r/gnoland
OFFICIAL_PACKAGES += ./gno.land/r/system

.PHONY: help
help:
@echo "Available make commands:"
Expand All @@ -15,6 +21,10 @@ build: precompile
test:
go run ../gnovm/cmd/gno test --verbose .

.PHONY: lint
lint:
go run ../gnovm/cmd/gno lint $(OFFICIAL_PACKAGES)

.PHONY: test.sync
test.sync:
go run ../gnovm/cmd/gno test --verbose --update-golden-tests .
Expand Down
6 changes: 6 additions & 0 deletions examples/gno.land/p/demo/grc/exts/vault/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module gno.land/p/demo/grc/exts/vault

require (
"gno.land/p/demo/avl" v0.0.0-latest
"gno.land/p/demo/grc/grc20" v0.0.0-latest
)
1 change: 1 addition & 0 deletions examples/gno.land/p/demo/merkle/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module gno.land/p/demo/merkle
7 changes: 7 additions & 0 deletions examples/gno.land/p/demo/microblog/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module gno.land/p/demo/microblog

require (
"gno.land/p/demo/avl" v0.0.0-latest
"gno.land/p/demo/ufmt" v0.0.0-latest
"gno.land/r/demo/users" v0.0.0-latest
)
6 changes: 6 additions & 0 deletions examples/gno.land/r/demo/microblog/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module gno.land/r/demo/microblog

require (
"gno.land/p/demo/microblog" v0.0.0-latest
"gno.land/r/demo/users" v0.0.0-latest
)
5 changes: 5 additions & 0 deletions examples/gno.land/r/demo/ui/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module gno.land/r/demo/ui

require (
"gno.land/p/demo/ui" v0.0.0-latest
)
3 changes: 0 additions & 3 deletions examples/gno.land/r/demo/x/upgrade/gno.mod

This file was deleted.

19 changes: 19 additions & 0 deletions examples/gno.land/r/x/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# `x/` Experimentation Area

Welcome to the `x/` directory, designed for experimentation.

Code in this area is less reviewed and more subject to change or break over
time. It's a great place to publish experiments and proposals for significant
challenges before they become an MVP and move to another directory.

While publishing code here provides core developers with more examples and edge
cases to work on through CI, consider alternative options such as working on
other repositories, keeping your work in a pull request, or exploring
[https://github.com/gnolang/hackerspace](https://github.com/gnolang/hackerspace)
for broader visibility and collaboration within the Gno community.

Exercise caution as code in this directory may be less stable or secure due to
its experimental nature.

Feel free to explore, experiment, and contribute to the exciting developments
happening in the `x/` directory. Together, we can shape the future of GnoVM.
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# r/x/outfmt
# r/x/manfred_outfmt

PoC of output formatting options.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module gno.land/r/demo/x/outfmt
module gno.land/r/x/manfred_outfmt

require (
"gno.land/p/demo/rand" v0.0.0-latest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ func genResult() Result {
func Render(path string) string {
if path == "" {
output := ""
output += "* [?fmt=stringer](/r/x/outfmt:?fmt=stringer)\n"
output += "* [?fmt=json](/r/x/outfmt:?fmt=json)\n"
output += "* [?fmt=jsonp](/r/x/outfmt:?fmt=jsonp)\n"
output += "* [?fmt=stringer](/r/x/manfred_outfmt:?fmt=stringer)\n"
output += "* [?fmt=json](/r/x/manfred_outfmt:?fmt=json)\n"
output += "* [?fmt=jsonp](/r/x/manfred_outfmt:?fmt=jsonp)\n"
return output
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ package outfmt
import (
"testing"

"gno.land/r/demo/x/outfmt"
"gno.land/r/x/manfred_outfmt"
)

func TestRender(t *testing.T) {
// home
{
got := outfmt.Render("")
expected := `* [?fmt=stringer](/r/x/outfmt:?fmt=stringer)
* [?fmt=json](/r/x/outfmt:?fmt=json)
* [?fmt=jsonp](/r/x/outfmt:?fmt=jsonp)
expected := `* [?fmt=stringer](/r/x/manfred_outfmt:?fmt=stringer)
* [?fmt=json](/r/x/manfred_outfmt:?fmt=json)
* [?fmt=jsonp](/r/x/manfred_outfmt:?fmt=jsonp)
`
if got != expected {
t.Fatalf("expected %q, got %q.", expected, got)
Expand Down
3 changes: 3 additions & 0 deletions examples/gno.land/r/x/manfred_upgrade_patterns/gno.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Draft

module gno.land/r/x/manfred_upgrade_patterns
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package upgradea

import (
v1 "gno.land/r/demo/x/upgrade/upgrade-a/v1"
v2 "gno.land/r/demo/x/upgrade/upgrade-a/v2"
v1 "gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v1"
v2 "gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v2"
)

func main() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package upgradea
import (
"strconv"

v1 "gno.land/r/demo/x/upgrade/upgrade-a/v1"
v1 "gno.land/r/x/manfred_upgrade_patterns/upgrade-a/v1"
)

var counter int
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package upgradeb
import (
"std"

v1 "gno.land/r/demo/x/upgrade/upgrade-b/v1"
v1 "gno.land/r/x/manfred_upgrade_patterns/upgrade-b/v1"
)

const admin = "blahblah"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package root

var (
counter int
currentImplementation = "gno.land/r/demo/x/upgrade/upgrade-c/v1"
currentImplementation = "gno.land/r/x/manfred_upgrade_patterns/upgrade-c/v1"
)

func Inc() int {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package v1

import "gno.land/r/demo/x/upgrade/upgrade-c/root"
import "gno.land/r/x/manfred_upgrade_patterns/upgrade-c/root"

func Inc() {
root.Inc()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package v1

import "gno.land/r/demo/x/upgrade/upgrade-c/root"
import "gno.land/r/x/manfred_upgrade_patterns/upgrade-c/root"

func Inc() {
root.Inc()
Expand Down
112 changes: 112 additions & 0 deletions gnovm/cmd/gno/lint.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package main

import (
"context"
"flag"
"fmt"
"os"
"path/filepath"

"github.com/gnolang/gno/tm2/pkg/commands"
osm "github.com/gnolang/gno/tm2/pkg/os"
)

type lintCfg struct {
verbose bool
rootDir string
setExitStatus int
// min_confidence: minimum confidence of a problem to print it (default 0.8)
// auto-fix: apply suggested fixes automatically.
}

func newLintCmd(io *commands.IO) *commands.Command {
cfg := &lintCfg{}

return commands.NewCommand(
commands.Metadata{
Name: "lint",
ShortUsage: "lint [flags] <package> [<package>...]",
ShortHelp: "Runs the linter for the specified packages",
},
cfg,
func(_ context.Context, args []string) error {
return execLint(cfg, args, io)
},
)
}

func (c *lintCfg) RegisterFlags(fs *flag.FlagSet) {
fs.BoolVar(&c.verbose, "verbose", false, "verbose output when lintning")
fs.StringVar(&c.rootDir, "root-dir", "", "clone location of github.com/gnolang/gno (gnodev tries to guess it)")
fs.IntVar(&c.setExitStatus, "set_exit_status", 1, "set exit status to 1 if any issues are found")
}

func execLint(cfg *lintCfg, args []string, io *commands.IO) error {
if len(args) < 1 {
return flag.ErrHelp
}

var (
verbose = cfg.verbose
rootDir = cfg.rootDir
)
if rootDir == "" {
rootDir = guessRootDir()
}

pkgPaths, err := gnoPackagesFromArgs(args)
if err != nil {
return fmt.Errorf("list packages from args: %w", err)
}

hasError := false
addIssue := func(issue lintIssue) {
hasError = true
fmt.Fprint(io.Err, issue.String()+"\n")
}

for _, pkgPath := range pkgPaths {
if verbose {
fmt.Fprintf(io.Err, "Linting %q...\n", pkgPath)
}

// 'gno.mod' exists?
gnoModPath := filepath.Join(pkgPath, "gno.mod")
if !osm.FileExists(gnoModPath) {
addIssue(lintIssue{
Code: lintNoGnoMod,
Confidence: 1,
Location: pkgPath,
Msg: "missing 'gno.mod' file",
})
}

// TODO: add more checkers
}

if hasError && cfg.setExitStatus != 0 {
os.Exit(cfg.setExitStatus)
}
return nil
}

type lintCode int

const (
lintUnknown lintCode = 0
lintNoGnoMod lintCode = iota
// TODO: add new linter codes here.
)

type lintIssue struct {
Code lintCode
Msg string
Confidence float64 // 1 is 100%
Location string // file:line, or equivalent
// TODO: consider writing fix suggestions
}

func (i lintIssue) String() string {
// TODO: consider crafting a doc URL based on Code.
return fmt.Sprintf("%s: %s (code=%d).", i.Location, i.Msg, i.Code)
}
31 changes: 31 additions & 0 deletions gnovm/cmd/gno/lint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package main

import "testing"

func TestLintApp(t *testing.T) {
tc := []testMainCase{
{
args: []string{"lint"},
errShouldBe: "flag: help requested",
}, {
args: []string{"lint", "--set_exit_status=0", "../../tests/integ/run-main/"},
stderrShouldContain: "./../../tests/integ/run-main: missing 'gno.mod' file (code=1).",
}, {
args: []string{"lint", "--set_exit_status=0", "../../tests/integ/run-main/"},
stderrShouldContain: "./../../tests/integ/run-main: missing 'gno.mod' file (code=1).",
}, {
args: []string{"lint", "--set_exit_status=0", "../../tests/integ/minimalist-gnomod/"},
// TODO: raise an error because there is a gno.mod, but no .gno files
}, {
args: []string{"lint", "--set_exit_status=0", "../../tests/integ/invalid-module-name/"},
// TODO: raise an error because gno.mod is invalid
},
// TODO: 'gno mod' is valid?
// TODO: is gno source valid?
// TODO: are dependencies valid?
// TODO: is gno source using unsafe/discouraged features?
// TODO: consider making `gno precompile; go lint *gen.go`
// TODO: check for imports of native libs from non _test.gno files
}
testMainCaseRun(t, tc)
}
5 changes: 3 additions & 2 deletions gnovm/cmd/gno/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,12 @@ func newGnocliCmd(io *commands.IO) *commands.Command {
)

cmd.AddSubCommands(
newModCmd(io),
newTestCmd(io),
newLintCmd(io),
newRunCmd(io),
newBuildCmd(io),
newPrecompileCmd(io),
newTestCmd(io),
newModCmd(io),
newCleanCmd(io),
newReplCmd(),
newDocCmd(io),
Expand Down
1 change: 1 addition & 0 deletions gnovm/docs/go-gno-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,3 +369,4 @@ Additional native types:
| go tool | | |
| go version | | |
| go vet | | |
| golint | gno lint | same intention |
2 changes: 1 addition & 1 deletion misc/docker-integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func runSuite(t *testing.T, tempdir string) {
var acc gnoland.GnoAccount
dockerExec_gnokeyQuery(t, "auth/accounts/"+test1Addr, &acc)
require.Equal(t, test1Addr, acc.Address.String(), "test1 account not found")
minCoins := std.MustParseCoins("9999900000000ugnot")
minCoins := std.MustParseCoins("9990000000000ugnot") // This value is chosen arbitrarily and may not be optimal. Feel free to update it to a more suitable amount
require.True(t, acc.Coins.IsAllGTE(minCoins),
"test1 account coins expected at least %s, got %s", minCoins, acc.Coins)

Expand Down
Loading