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: v1 #1

Merged
merged 4 commits into from
Sep 22, 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
4 changes: 4 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# CODEOWNERS: https://help.github.com/articles/about-codeowners/

# Primary repo maintainers.
* @moul
18 changes: 18 additions & 0 deletions .github/workflows/auto-author-assign.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: auto-author-assign

on:
pull_request_target:
types: [ opened, reopened ]

permissions:
pull-requests: write

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
assign-author:
runs-on: ubuntu-latest
steps:
- uses: toshimaru/auto-author-assign@v1.6.2
83 changes: 83 additions & 0 deletions .github/workflows/gnoffee.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: gnoffee

on:
pull_request:
paths:
- "go.sum"
- "**.go"
- "**.gnoffee"
- ".github/workflows/gnoffee.yml"
push:
branches: [ "main" ]

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
build:
strategy:
fail-fast: false
matrix:
go-version:
- "1.20.x"
- "1.21.x"
goarch:
- "amd64"
goos:
- "linux"
program:
- "gnoffee"
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
- uses: actions/checkout@v3
- name: go install
run: GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go install ./cmd/${{ matrix.program }}

test:
strategy:
fail-fast: false
matrix:
go-version:
- "1.20.x"
- "1.21.x"
args:
- "test"
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/setup-go@v4
with:
go-version: ${{ matrix.go-version }}
- uses: actions/checkout@v3
- name: test
run: |
export GOPATH=$HOME/go
export GOTEST_FLAGS="-v -p 1 -timeout=30m -coverprofile=coverage.out -covermode=atomic"
make ${{ matrix.args }}
#- if: runner.os == 'Linux'
# uses: codecov/codecov-action@v3
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
# name: gnoffee
# flags: gnoffee,gnoffee-${{matrix.args}},go-${{ matrix.go-version }}
# files: ./coverage.out
# #fail_ci_if_error: ${{ github.repository == 'gnolang/gnoffee' }}
# fail_ci_if_error: false # temporarily

#docker-integration:
# strategy:
# fail-fast: false
# runs-on: ubuntu-latest
# timeout-minutes: 10
# steps:
# - uses: actions/checkout@v3
# # TODO: setup docker caching
# - run: make test.docker
# - run: docker logs int_gnoffee || true

# TODO: docker-less integration test?
20 changes: 20 additions & 0 deletions .github/workflows/lint-pr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: "lint-pr"

on:
pull_request_target:
types:
- opened
- edited
- synchronize

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

jobs:
pr-title:
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
install:
go install ./cmd/...

test:
go test -v ./...
141 changes: 141 additions & 0 deletions cmd/gnoffee/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package main

import (
"flag"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"io/ioutil"
"os"
"path/filepath"

"github.com/gnolang/gnoffee/pkg/gnoffee"
)

var writeFlag bool

func init() {
flag.BoolVar(&writeFlag, "w", false, "write result to gnoffee.gen.go file instead of stdout")
}

func main() {
flag.Parse()
args := flag.Args()

if len(args) < 1 {
fmt.Fprintln(os.Stderr, "Usage: gnoffee [-w] <package-path or file.gnoffee or '-'>")
return
}

err := doMain(args[0])
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(1)
}
}

func doMain(arg string) error {
fset, pkg, err := processPackageOrFileOrStdin(arg)
if err != nil {
return fmt.Errorf("parse error: %w", err)
}

newFile, err := gnoffee.Stage2(pkg)
if err != nil {
return fmt.Errorf("processing the AST: %w", err)
}

// combine existing files into newFile to generate a unique file for the whole package.
for _, file := range pkg {
newFile.Decls = append(newFile.Decls, file.Decls...)
}

// Create a new package comment.
commentText := "// Code generated by \"gnoffee\". DO NOT EDIT."

if writeFlag {
filename := "gnoffee.gen.go"
f, err := os.Create(filename)
if err != nil {
return fmt.Errorf("creating file %q: %w", filename, err)
}
defer f.Close()

_, err = fmt.Fprintln(f, commentText)
if err != nil {
return fmt.Errorf("writing to file %q: %w", filename, err)
}
err = printer.Fprint(f, fset, newFile)
if err != nil {
return fmt.Errorf("writing to file %q: %w", filename, err)
}
} else {
_, _ = fmt.Println(commentText)
_ = printer.Fprint(os.Stdout, fset, newFile)
}
return nil
}

func processPackageOrFileOrStdin(arg string) (*token.FileSet, map[string]*ast.File, error) {
fset := token.NewFileSet()
pkg := map[string]*ast.File{}

processFile := func(data []byte, filename string) error {
source := string(data)
source = gnoffee.Stage1(source)

parsedFile, err := parser.ParseFile(fset, filename, source, parser.ParseComments)
if err != nil {
return fmt.Errorf("parsing file %q: %w", filename, err)
}
pkg[filename] = parsedFile
return nil
}

// process arg
if arg == "-" {
// Read from stdin and process
data, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return nil, nil, fmt.Errorf("reading from stdin: %w", err)
}
if err := processFile(data, "stdin.gnoffee"); err != nil {
return nil, nil, err
}
} else {
// If it's a directory, gather all .go and .gnoffee files and process accordingly
if info, err := os.Stat(arg); err == nil && info.IsDir() {
err := filepath.Walk(arg, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}

ext := filepath.Ext(path)
if ext == ".gnoffee" {
data, err := ioutil.ReadFile(path)
if err != nil {
return fmt.Errorf("reading file %q: %w", path, err)
}
if err := processFile(data, path); err != nil {
return err
}
}
return nil
})
if err != nil {
return nil, nil, err
}
} else {
data, err := ioutil.ReadFile(arg)
if err != nil {
return nil, nil, fmt.Errorf("reading file %q: %w", arg, err)
}
if err := processFile(data, arg); err != nil {
return nil, nil, err
}
}
}
return fset, pkg, nil
}
50 changes: 50 additions & 0 deletions cmd/gnoffee/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package main

import (
"os/exec"
"path/filepath"
"testing"

"github.com/jaekwon/testify/require"
"github.com/rogpeppe/go-internal/testscript"
)

func TestTest(t *testing.T) {
testscript.Run(t, setupTestScript(t, "testdata"))
}

func setupTestScript(t *testing.T, txtarDir string) testscript.Params {
t.Helper()
// Get root location of github.com/gnolang/gnoffee
goModPath, err := exec.Command("go", "env", "GOMOD").CombinedOutput()
require.NoError(t, err)
rootDir := filepath.Dir(string(goModPath))
// Build a fresh gno binary in a temp directory
gnoffeeBin := filepath.Join(t.TempDir(), "gnoffee")
err = exec.Command("go", "build", "-o", gnoffeeBin, filepath.Join(rootDir, "cmd", "gnoffee")).Run()
require.NoError(t, err)
// Define script params
return testscript.Params{
Setup: func(env *testscript.Env) error {
return nil
},
Cmds: map[string]func(ts *testscript.TestScript, neg bool, args []string){
// add a custom "gnoffee" command so txtar files can easily execute "gno"
// without knowing where is the binary or how it is executed.
"gnoffee": func(ts *testscript.TestScript, neg bool, args []string) {
err := ts.Exec(gnoffeeBin, args...)
if err != nil {
ts.Logf("[%v]\n", err)
if !neg {
ts.Fatalf("unexpected gnoffee command failure")
}
} else {
if neg {
ts.Fatalf("unexpected gnoffee command success")
}
}
},
},
Dir: txtarDir,
}
}
61 changes: 61 additions & 0 deletions cmd/gnoffee/testdata/valid_sample_with_export.txtar
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Test with a valid sample.gnoffee

gnoffee -w .

! stderr .+
! stdout .+

cmp gen.golden gnoffee.gen.go

-- sample.gnoffee --
package sample

type foo struct{}

export baz as Bar

var baz = foo{}

func (f *foo) Hello() string {
return "Hello from foo!"
}

func (f *foo) Bye() {
println("Goodbye from foo!")
}

type Bar interface {
Hello() string
Bye()
}

-- gen.golden --
// Code generated by "gnoffee". DO NOT EDIT.
package sample

// This function was generated by gnoffee due to the export directive.
func Hello() string {
return baz.Hello()
}

// This function was generated by gnoffee due to the export directive.
func Bye() {
baz.Bye()
}

type foo struct{}

var baz = foo{}

func (f *foo) Hello() string {
return "Hello from foo!"
}

func (f *foo) Bye() {
println("Goodbye from foo!")
}

type Bar interface {
Hello() string
Bye()
}
Loading