Repository: mbrt/gmailctl
Pull request #211 (2021-11-07 21:10:49)

Move to Go 1.17
Make Go 1.17 the minimum supported version. This allows to drop `go-bindata` and rely on the native `embed` package.


Patch:
diff --git a/.github/release.yml b/.github/release.yml
new file mode 100644
index 0000000..da58db7
--- /dev/null
+++ b/.github/release.yml
@@ -0,0 +1,14 @@
+changelog:
+  exclude:
+    labels:
+      - kind/dependencies
+  categories:
+    - title: Breaking Changes
+      labels:
+        - breaking-change
+    - title: New features
+      labels:
+        - kind/feature
+    - title: Maintenance and bug fixing
+      labels:
+        - "*"
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index 3a961ed..ac557d2 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -28,9 +28,7 @@ jobs:
         version: v1.34
 
     - name: Test
-      run: |
-        go test -v ./...
-        hack/check-codegen.sh
+      run: go test -v ./...
 
     - name: Coverage
       id: coverage
diff --git a/cmd/gmailctl-config-migrate/import.go b/cmd/gmailctl-config-migrate/import.go
index 540d1e8..cd000b4 100644
--- a/cmd/gmailctl-config-migrate/import.go
+++ b/cmd/gmailctl-config-migrate/import.go
@@ -10,7 +10,7 @@ import (
 	"github.com/mbrt/gmailctl/cmd/gmailctl-config-migrate/v1alpha1"
 	"github.com/mbrt/gmailctl/cmd/gmailctl-config-migrate/v1alpha2"
 	"github.com/mbrt/gmailctl/cmd/gmailctl-config-migrate/v1alpha3"
-	"github.com/mbrt/gmailctl/internal/rimport"
+	"github.com/mbrt/gmailctl/internal/engine/rimport"
 )
 
 const header = `// This file is generated by gmailctl-config-migrate.
diff --git a/cmd/gmailctl-config-migrate/v1alpha1/config.go b/cmd/gmailctl-config-migrate/v1alpha1/config.go
index dfc30ae..1d0ec2a 100644
--- a/cmd/gmailctl-config-migrate/v1alpha1/config.go
+++ b/cmd/gmailctl-config-migrate/v1alpha1/config.go
@@ -1,7 +1,7 @@
 package v1alpha1
 
 import (
-	"github.com/mbrt/gmailctl/internal/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
 )
 
 // Version is the config format version supported by this package.
diff --git a/cmd/gmailctl-config-migrate/v1alpha2/config.go b/cmd/gmailctl-config-migrate/v1alpha2/config.go
index 3420ff7..521699c 100644
--- a/cmd/gmailctl-config-migrate/v1alpha2/config.go
+++ b/cmd/gmailctl-config-migrate/v1alpha2/config.go
@@ -6,7 +6,7 @@ import (
 	"strings"
 
 	"github.com/mbrt/gmailctl/cmd/gmailctl-config-migrate/v1alpha1"
-	"github.com/mbrt/gmailctl/internal/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
 )
 
 // Version is the latest supported version.
diff --git a/cmd/gmailctl-config-migrate/v1alpha3/alias.go b/cmd/gmailctl-config-migrate/v1alpha3/alias.go
index cb3ba02..83c3450 100644
--- a/cmd/gmailctl-config-migrate/v1alpha3/alias.go
+++ b/cmd/gmailctl-config-migrate/v1alpha3/alias.go
@@ -1,6 +1,6 @@
 package v1alpha3
 
-import cfg "github.com/mbrt/gmailctl/internal/config/v1alpha3"
+import cfg "github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 
 // Config is an alias pointing to the actual v1alpha3.Config type.
 type Config = cfg.Config
diff --git a/cmd/gmailctl-config-migrate/v1alpha3/import.go b/cmd/gmailctl-config-migrate/v1alpha3/import.go
index e4270dd..ee1b7c3 100644
--- a/cmd/gmailctl-config-migrate/v1alpha3/import.go
+++ b/cmd/gmailctl-config-migrate/v1alpha3/import.go
@@ -6,7 +6,7 @@ import (
 	"github.com/hashicorp/go-multierror"
 
 	"github.com/mbrt/gmailctl/cmd/gmailctl-config-migrate/v1alpha2"
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 )
 
 var dummyFilter = v1alpha3.FilterNode{}
diff --git a/cmd/gmailctl/cmd/api_provider.go b/cmd/gmailctl/cmd/api_provider.go
index 862f524..3fc86bd 100644
--- a/cmd/gmailctl/cmd/api_provider.go
+++ b/cmd/gmailctl/cmd/api_provider.go
@@ -6,7 +6,7 @@ import (
 
 	"google.golang.org/api/gmail/v1"
 
-	"github.com/mbrt/gmailctl/internal/api"
+	"github.com/mbrt/gmailctl/internal/engine/api"
 )
 
 // APIProvider is the APIProvider used by all gmailctl commands.
diff --git a/cmd/gmailctl/cmd/apply_cmd.go b/cmd/gmailctl/cmd/apply_cmd.go
index 42194b9..d9e64da 100644
--- a/cmd/gmailctl/cmd/apply_cmd.go
+++ b/cmd/gmailctl/cmd/apply_cmd.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/spf13/cobra"
 
-	papply "github.com/mbrt/gmailctl/internal/apply"
+	papply "github.com/mbrt/gmailctl/internal/engine/apply"
 	"github.com/mbrt/gmailctl/internal/errors"
 )
 
diff --git a/cmd/gmailctl/cmd/bindata.go b/cmd/gmailctl/cmd/bindata.go
deleted file mode 100644
index 623e8f9..0000000
--- a/cmd/gmailctl/cmd/bindata.go
+++ /dev/null
@@ -1,273 +0,0 @@
-// Code generated for package cmd by go-bindata DO NOT EDIT. (@generated)
-// sources:
-// ../../../gmailctl.libsonnet
-// ../../../default-config.jsonnet
-package cmd
-
-import (
-	"bytes"
-	"compress/gzip"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-	"strings"
-	"time"
-)
-
-func bindataRead(data []byte, name string) ([]byte, error) {
-	gz, err := gzip.NewReader(bytes.NewBuffer(data))
-	if err != nil {
-		return nil, fmt.Errorf("Read %q: %v", name, err)
-	}
-
-	var buf bytes.Buffer
-	_, err = io.Copy(&buf, gz)
-	clErr := gz.Close()
-
-	if err != nil {
-		return nil, fmt.Errorf("Read %q: %v", name, err)
-	}
-	if clErr != nil {
-		return nil, err
-	}
-
-	return buf.Bytes(), nil
-}
-
-type asset struct {
-	bytes []byte
-	info  os.FileInfo
-}
-
-type bindataFileInfo struct {
-	name    string
-	size    int64
-	mode    os.FileMode
-	modTime time.Time
-}
-
-// Name return file name
-func (fi bindataFileInfo) Name() string {
-	return fi.name
-}
-
-// Size return file size
-func (fi bindataFileInfo) Size() int64 {
-	return fi.size
-}
-
-// Mode return file mode
-func (fi bindataFileInfo) Mode() os.FileMode {
-	return fi.mode
-}
-
-// Mode return file modify time
-func (fi bindataFileInfo) ModTime() time.Time {
-	return fi.modTime
-}
-
-// IsDir return file whether a directory
-func (fi bindataFileInfo) IsDir() bool {
-	return fi.mode&os.ModeDir != 0
-}
-
-// Sys return file is sys mode
-func (fi bindataFileInfo) Sys() interface{} {
-	return nil
-}
-
-var _GmailctlLibsonnet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x9c\x55\x4d\x6f\xe3\x36\x10\xbd\xeb\x57\x3c\xec\xc5\x12\xa2\xda\xbb\x57\x07\x29\xb0\xdd\xa2\xe8\xa1\xd8\xf6\x10\xa0\x07\x41\x07\x5a\x1a\x59\x53\xd0\xa4\x4b\x8e\xd6\x31\x82\xfc\xf7\x82\x1f\x92\x95\x6c\xf6\xd2\x8b\x21\x73\x66\xde\x23\xdf\x9b\x21\x77\x3b\x1c\x4f\x8a\x75\x27\x1a\x5e\x94\xe9\x95\xeb\xa1\xf9\xe0\x94\xbb\x16\xbb\x5d\xb1\xdb\xe1\x71\x64\x3f\x2f\xa1\xb3\x46\x14\x1b\x8f\x49\x58\xb3\x30\x79\x0c\xd6\xa1\xb3\xa7\x93\x35\x18\x58\x0b\x39\xd8\x33\x39\x25\x6c\x8d\xdf\x16\xc5\x73\x01\xec\x76\xe8\x46\xc5\xe6\xb7\x18\xf7\x60\x0f\x85\x61\x32\x5d\x48\x82\x8c\x4a\x6a\x1c\xf9\x1b\x19\x28\x68\xf6\x02\x3b\xc0\x4d\x9a\x7c\x9d\x8a\x1d\xc9\xe4\x4c\x28\x32\x74\x49\x19\x97\x91\x1c\x41\x46\x4a\x89\x50\x8e\x12\x07\xf5\x10\x7b\x1c\x49\xc8\xe5\xea\xcb\xc8\xdd\x88\x13\x29\xe3\x23\x57\xa8\xba\xa2\x53\x06\x07\x02\x1b\x21\x77\x76\x24\xd4\x43\x05\x86\x08\x92\x0a\xed\x80\x0f\x3c\x80\xb4\x9f\x7f\x3f\x6c\x53\xe4\x31\xf0\x92\x9f\xb4\x84\xb3\x1c\x94\xe7\x4e\x69\x7d\x7d\xbb\xfd\xbc\x4b\x52\xdd\x98\xb5\x49\xe5\xec\x71\xb2\x3d\x0f\x4c\x3d\x0e\x57\xa8\xbe\x67\x73\x84\x32\xf8\xfc\xf5\x57\x5c\x58\xc6\x78\x30\x43\xc7\xa8\x62\x80\x53\x5a\x87\xb5\x54\x7e\x76\xf4\x8d\xed\xe4\x33\xa6\x0f\x9b\x5a\xeb\x5b\x0e\xbe\xda\xef\x0b\x20\x66\x27\xa7\xae\xe9\xe8\xb3\xca\x61\x7b\x60\x59\x94\x65\xf1\x89\x8f\xfa\x8c\xba\x8d\xf5\xda\x76\x4a\xe7\x48\xe9\x2a\x3c\xe0\x19\xc6\xca\x1e\x6e\x9b\xcd\x7e\xb9\x9f\x89\x1c\x75\x93\xf3\xfc\x8d\x32\x95\x25\x0f\x71\x76\x3a\x8e\x71\xfb\xa4\xe9\x44\x46\x7c\x3c\x8e\x73\x2b\x78\x35\x3d\x95\xca\xb9\x1a\x5c\xcf\x9b\xa8\xe1\x26\x63\xd8\x1c\x2b\x3c\xc4\x4c\x80\x07\x30\x7e\x7e\x80\x97\x7e\xab\xc9\x1c\x65\x0c\x45\x55\x90\xc5\xe4\x14\xcc\x55\xf9\x3f\x69\x4f\x4b\x68\xb7\xcb\xaa\x5e\xf2\xe9\xfd\xac\xb8\x1d\xf6\xeb\xac\x9f\x7e\xa4\xfe\x4d\xf8\xe8\xee\xf7\x35\xdd\xe4\x1c\x19\x89\xe1\x25\x3a\x4b\x78\x71\x41\xbd\x65\x19\x59\xe7\xfd\xab\x35\x40\x99\x7e\xbf\x58\x71\x87\x46\x39\xd7\x70\x9b\xd5\x6e\xeb\x55\xee\xcb\xfa\x8f\x8a\xb3\xe4\xf7\xc8\xf9\xf9\xff\x2d\x25\xfb\x14\x73\x17\xbd\x71\x87\x4f\xf5\x9a\x2d\x3b\x9d\x40\xaa\x76\xb1\x21\xc5\x2e\xae\xad\x20\x8a\xb5\x17\xc7\x9d\xdc\x17\x45\x36\x66\xe5\xc9\xe0\x2b\x3c\x3c\xe0\x63\xf4\x05\x4d\x5b\xcc\x46\x44\xda\xc1\xd7\x81\x71\xe6\x19\x7c\xf3\x31\xd2\x34\xf1\xab\xad\xea\x22\xb5\x78\xcf\x8e\x3a\xd1\xd7\x47\x8b\x93\x92\x6e\x24\x0f\x6b\xf4\x15\x14\x2e\xab\xf5\xf0\x53\xc7\x67\x0e\x9a\xb3\x07\x9b\xb8\xb6\x79\xfc\x73\x93\x50\x06\x26\xdd\xd7\xa1\x61\x53\xe0\xcb\x97\x0d\xac\xc3\xe6\x97\xf8\x61\x28\x4e\xce\x8d\xaa\x5c\xd0\xaa\xfd\x6c\x4b\xb4\xa3\xc9\xd2\x3d\x43\xec\x7e\xc5\xb9\x38\x90\x87\xe2\x19\x5d\xf7\x2a\xfe\x4e\xc6\xe1\x07\x29\xd1\xda\x97\x78\xfe\xd4\x31\xf4\x24\x64\xfa\xbf\x59\xc6\xbf\x54\xe8\x2a\x5f\x6a\x75\x20\xed\xe7\x89\x58\x67\x95\xe7\xdb\x9c\xa4\xf5\xce\x9e\xce\x1e\x69\x5a\xfc\x59\xb3\x94\xe7\x1a\x9b\xdd\xa6\xba\x7f\x95\x16\x3c\x09\x5d\x5b\xc3\xd1\xe9\x86\xf1\xd6\xd4\x14\x9c\x5d\x5d\xb5\x5d\x13\x8a\xdb\x65\xe1\xd5\xc0\x61\x41\xc7\x5d\xa0\xc6\x5d\x20\x69\x3e\xb6\x91\xac\xf9\xb4\x6f\xab\xd0\x56\x11\xe1\xbe\x78\x8f\x36\x1e\x22\x12\x7f\xca\xed\x74\x6e\x57\xa3\x9d\xf0\x43\x4e\x04\x4d\x5f\x01\x36\xa1\x05\xa0\x41\x2b\x11\x32\x9f\x9d\x53\x57\x5f\x36\x59\x2d\x5d\xc5\x57\x4b\x87\x8e\x49\xa2\xde\x3a\x2f\xcc\xee\x1f\x71\xed\xf6\xec\xe4\xf1\x4f\xa9\x30\x44\x7d\xba\xb8\xe7\xe7\x67\xbb\x98\x76\xab\x2e\xc3\xe7\xac\x68\x3e\x96\x3d\xfc\x43\x9d\xfc\xae\x52\x70\x99\x51\x6c\x12\xf2\xe6\xd5\x65\xf6\xbd\xff\xeb\xa2\x6d\x6e\x86\xe2\x8d\xec\x4d\xfb\x7f\x0f\x92\x9e\x86\x78\xb3\xa5\x57\x2e\xc1\x7c\xb5\x92\x2f\x74\x63\x61\x65\x24\x87\xb3\x0b\x2f\x7c\x78\xfc\xeb\xbc\x22\xa3\x4a\xb3\x67\xd4\x89\xea\xf8\x18\x2b\x11\xd5\x8d\xd4\x27\x14\xb1\x6b\xe2\x92\xb7\xb4\x4d\x37\xa6\xd5\xd6\x55\x81\x2f\x12\xaf\xa4\x5b\x9e\xb0\x24\x6c\xae\x4c\x0d\x3d\x19\xfe\xb7\x8c\x9d\x6d\x9d\x94\xef\xf8\xbc\xb2\xe1\x29\x79\xfd\x14\xbc\x8e\xb8\x6d\x55\xe5\x06\x69\x9e\xe3\x7e\xf7\x78\xc2\xcb\x2d\x29\x37\x44\x5d\xbc\x14\xff\x05\x00\x00\xff\xff\x5e\x41\xc2\xaf\x1e\x09\x00\x00")
-
-func GmailctlLibsonnetBytes() ([]byte, error) {
-	return bindataRead(
-		_GmailctlLibsonnet,
-		"../../../gmailctl.libsonnet",
-	)
-}
-
-func GmailctlLibsonnet() (*asset, error) {
-	bytes, err := GmailctlLibsonnetBytes()
-	if err != nil {
-		return nil, err
-	}
-
-	info := bindataFileInfo{name: "../../../gmailctl.libsonnet", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
-	a := &asset{bytes: bytes, info: info}
-	return a, nil
-}
-
-var _DefaultConfigJsonnet = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x6c\x91\x41\x6b\xe3\x48\x10\x85\xef\xfa\x15\x6f\xd9\x83\x2e\xc6\x62\xd9\x9b\x42\x20\x81\x18\x36\xb0\x8e\x43\xc6\x39\x0c\x43\x0e\x25\xb9\x64\x35\xd3\xea\x12\xd5\x25\x4f\x4c\xf0\x7f\x1f\x5a\xed\x24\x93\x90\x53\xbb\xba\x9f\xbf\x57\xef\xa9\xaa\x70\xb7\xd9\xae\x6a\x6c\x7b\x17\xe1\x22\x08\xd1\x0d\xa3\x67\xf0\x33\xa5\x73\x59\x54\x15\xee\x3d\x53\x64\x28\x77\xac\x30\x41\x6f\x36\xc6\xba\xaa\xf6\xce\xfa\xa9\x59\xb6\x32\x54\x43\xa3\x56\xed\x07\x72\xbe\x35\xff\x77\x2b\xa1\x73\xfb\x49\xc9\x9c\x04\x74\xa2\xd8\x49\x1b\x41\x8d\x4c\x96\x80\xd6\x33\xb2\x26\x3d\x0e\x64\x4b\xdc\x48\x28\x2d\x4d\x7b\xb6\xe4\xd1\xf6\x14\xf6\xfc\x87\xf2\x95\xd6\x70\x27\xca\x49\x42\xe3\xe8\x8f\x70\x99\x28\x38\xca\xa4\x90\x5f\x01\x2e\x34\xf2\xfc\x57\x91\xae\x6f\x87\x51\xd4\x66\x4a\x34\x0a\x3b\xd2\x1d\xbc\x6b\x94\xf4\x58\x78\x69\xc9\xa7\x09\x97\x70\x59\x57\xbe\x26\x58\x7a\xd7\x44\x09\x81\xad\xbc\x98\x41\xdf\x64\x60\x4c\x91\xbb\xc9\xe3\x40\xea\xa8\xf1\x1c\x21\x01\x26\x63\x7a\xdf\x6e\x6e\x36\x35\xee\x27\xcb\x6b\x70\xe2\xa0\x67\xe5\xb3\xcd\xc0\xb8\x44\xf9\x7d\xf3\xf8\xb0\x5c\xad\xaf\x6f\xff\xbf\x9a\x9d\x52\x75\xe5\xc5\x59\x62\xb2\x4e\xa2\x17\x98\xd4\x49\x7f\xba\x28\x66\xeb\x6d\xcf\xa0\xd6\x26\xf2\x1f\x9b\x28\x5e\x0a\xa0\xaa\xb0\x4e\xb9\x4c\xf4\x88\x9e\x69\xc7\x5a\x00\x07\xd6\xe8\x24\xd4\x28\x0f\xff\x90\x1f\x7b\xfa\xb7\x5c\x14\x00\x4d\xd6\x8b\xd6\x48\x7f\x04\x02\x0d\x5c\xe7\x9d\x70\x77\xbd\x5e\xe1\xbf\xd5\xc3\x6a\xd6\x21\xef\x9f\xb6\x48\xe3\x69\x51\x64\xa7\x1c\xf2\x31\xf2\x7b\xd7\x3a\xa5\x1e\xe6\xa0\xc8\x43\x8d\x1f\x33\x22\x9b\x00\x9d\xf3\xc6\x5a\xcf\xf1\x16\xe7\x3b\x6a\x53\x80\x58\xbf\x89\x80\x81\xf4\x67\xfe\x5a\x14\xac\x86\xe9\xf4\xa6\x3e\xe5\x1f\xe7\xe3\x33\xf7\x1d\xd1\xa9\x0c\x35\xca\x86\xf4\xea\x48\xbd\xc8\xdc\xee\x27\xc8\x97\xde\xa4\x6d\xef\x0e\xfc\xd1\x15\xf0\xd4\xb0\x4f\x79\xca\x4e\xa4\x7c\xfa\x6a\x9d\xa7\x45\x71\x2a\x7e\x07\x00\x00\xff\xff\x9f\xa5\x47\x2f\x45\x03\x00\x00")
-
-func DefaultConfigJsonnetBytes() ([]byte, error) {
-	return bindataRead(
-		_DefaultConfigJsonnet,
-		"../../../default-config.jsonnet",
-	)
-}
-
-func DefaultConfigJsonnet() (*asset, error) {
-	bytes, err := DefaultConfigJsonnetBytes()
-	if err != nil {
-		return nil, err
-	}
-
-	info := bindataFileInfo{name: "../../../default-config.jsonnet", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
-	a := &asset{bytes: bytes, info: info}
-	return a, nil
-}
-
-// Asset loads and returns the asset for the given name.
-// It returns an error if the asset could not be found or
-// could not be loaded.
-func Asset(name string) ([]byte, error) {
-	cannonicalName := strings.Replace(name, "\\", "/", -1)
-	if f, ok := _bindata[cannonicalName]; ok {
-		a, err := f()
-		if err != nil {
-			return nil, fmt.Errorf("Asset %s can't read by error: %v", name, err)
-		}
-		return a.bytes, nil
-	}
-	return nil, fmt.Errorf("Asset %s not found", name)
-}
-
-// MustAsset is like Asset but panics when Asset would return an error.
-// It simplifies safe initialization of global variables.
-func MustAsset(name string) []byte {
-	a, err := Asset(name)
-	if err != nil {
-		panic("asset: Asset(" + name + "): " + err.Error())
-	}
-
-	return a
-}
-
-// AssetInfo loads and returns the asset info for the given name.
-// It returns an error if the asset could not be found or
-// could not be loaded.
-func AssetInfo(name string) (os.FileInfo, error) {
-	cannonicalName := strings.Replace(name, "\\", "/", -1)
-	if f, ok := _bindata[cannonicalName]; ok {
-		a, err := f()
-		if err != nil {
-			return nil, fmt.Errorf("AssetInfo %s can't read by error: %v", name, err)
-		}
-		return a.info, nil
-	}
-	return nil, fmt.Errorf("AssetInfo %s not found", name)
-}
-
-// AssetNames returns the names of the assets.
-func AssetNames() []string {
-	names := make([]string, 0, len(_bindata))
-	for name := range _bindata {
-		names = append(names, name)
-	}
-	return names
-}
-
-// _bindata is a table, holding each asset generator, mapped to its name.
-var _bindata = map[string]func() (*asset, error){
-	"../../../gmailctl.libsonnet":     GmailctlLibsonnet,
-	"../../../default-config.jsonnet": DefaultConfigJsonnet,
-}
-
-// AssetDir returns the file names below a certain
-// directory embedded in the file by go-bindata.
-// For example if you run go-bindata on data/... and data contains the
-// following hierarchy:
-//     data/
-//       foo.txt
-//       img/
-//         a.png
-//         b.png
-// then AssetDir("data") would return []string{"foo.txt", "img"}
-// AssetDir("data/img") would return []string{"a.png", "b.png"}
-// AssetDir("foo.txt") and AssetDir("notexist") would return an error
-// AssetDir("") will return []string{"data"}.
-func AssetDir(name string) ([]string, error) {
-	node := _bintree
-	if len(name) != 0 {
-		cannonicalName := strings.Replace(name, "\\", "/", -1)
-		pathList := strings.Split(cannonicalName, "/")
-		for _, p := range pathList {
-			node = node.Children[p]
-			if node == nil {
-				return nil, fmt.Errorf("Asset %s not found", name)
-			}
-		}
-	}
-	if node.Func != nil {
-		return nil, fmt.Errorf("Asset %s not found", name)
-	}
-	rv := make([]string, 0, len(node.Children))
-	for childName := range node.Children {
-		rv = append(rv, childName)
-	}
-	return rv, nil
-}
-
-type bintree struct {
-	Func     func() (*asset, error)
-	Children map[string]*bintree
-}
-
-var _bintree = &bintree{nil, map[string]*bintree{
-	"..": &bintree{nil, map[string]*bintree{
-		"..": &bintree{nil, map[string]*bintree{
-			"..": &bintree{nil, map[string]*bintree{
-				"default-config.jsonnet": &bintree{DefaultConfigJsonnet, map[string]*bintree{}},
-				"gmailctl.libsonnet":     &bintree{GmailctlLibsonnet, map[string]*bintree{}},
-			}},
-		}},
-	}},
-}}
-
-// RestoreAsset restores an asset under the given directory
-func RestoreAsset(dir, name string) error {
-	data, err := Asset(name)
-	if err != nil {
-		return err
-	}
-	info, err := AssetInfo(name)
-	if err != nil {
-		return err
-	}
-	err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
-	if err != nil {
-		return err
-	}
-	err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
-	if err != nil {
-		return err
-	}
-	err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-// RestoreAssets restores an asset under the given directory recursively
-func RestoreAssets(dir, name string) error {
-	children, err := AssetDir(name)
-	// File
-	if err != nil {
-		return RestoreAsset(dir, name)
-	}
-	// Dir
-	for _, child := range children {
-		err = RestoreAssets(dir, filepath.Join(name, child))
-		if err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-func _filePath(dir, name string) string {
-	cannonicalName := strings.Replace(name, "\\", "/", -1)
-	return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
-}
diff --git a/cmd/gmailctl/cmd/config.go b/cmd/gmailctl/cmd/config.go
index 4aa2c5b..b98704b 100644
--- a/cmd/gmailctl/cmd/config.go
+++ b/cmd/gmailctl/cmd/config.go
@@ -6,10 +6,10 @@ import (
 	"os"
 	"path"
 
-	papply "github.com/mbrt/gmailctl/internal/apply"
-	"github.com/mbrt/gmailctl/internal/cfgtest"
-	"github.com/mbrt/gmailctl/internal/config"
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	papply "github.com/mbrt/gmailctl/internal/engine/apply"
+	"github.com/mbrt/gmailctl/internal/engine/cfgtest"
+	"github.com/mbrt/gmailctl/internal/engine/config"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 )
 
 type parseResult struct {
diff --git a/cmd/gmailctl/cmd/data.go b/cmd/gmailctl/cmd/data.go
deleted file mode 100644
index 386785d..0000000
--- a/cmd/gmailctl/cmd/data.go
+++ /dev/null
@@ -1,13 +0,0 @@
-//go:generate go run github.com/go-bindata/go-bindata/go-bindata -nometadata -pkg cmd ../../../gmailctl.libsonnet ../../../default-config.jsonnet
-
-package cmd
-
-// GmailctlLib returns the embedded gmailctl.libsonnet file
-func GmailctlLib() string {
-	return string(MustAsset("../../../gmailctl.libsonnet"))
-}
-
-// DefaultConfig returns the embedded default configuration file
-func DefaultConfig() string {
-	return string(MustAsset("../../../default-config.jsonnet"))
-}
diff --git a/cmd/gmailctl/cmd/debug_cmd.go b/cmd/gmailctl/cmd/debug_cmd.go
index 0d028fb..f3d9a62 100644
--- a/cmd/gmailctl/cmd/debug_cmd.go
+++ b/cmd/gmailctl/cmd/debug_cmd.go
@@ -7,7 +7,7 @@ import (
 	"github.com/spf13/cobra"
 	"gopkg.in/yaml.v2"
 
-	"github.com/mbrt/gmailctl/internal/filter"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
 )
 
 var (
diff --git a/cmd/gmailctl/cmd/diff_cmd.go b/cmd/gmailctl/cmd/diff_cmd.go
index bc0a92b..9980eec 100644
--- a/cmd/gmailctl/cmd/diff_cmd.go
+++ b/cmd/gmailctl/cmd/diff_cmd.go
@@ -5,7 +5,7 @@ import (
 
 	"github.com/spf13/cobra"
 
-	papply "github.com/mbrt/gmailctl/internal/apply"
+	papply "github.com/mbrt/gmailctl/internal/engine/apply"
 )
 
 var (
diff --git a/cmd/gmailctl/cmd/download_cmd.go b/cmd/gmailctl/cmd/download_cmd.go
index b15ab5d..6dafd4d 100644
--- a/cmd/gmailctl/cmd/download_cmd.go
+++ b/cmd/gmailctl/cmd/download_cmd.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/spf13/cobra"
 
-	"github.com/mbrt/gmailctl/internal/rimport"
+	"github.com/mbrt/gmailctl/internal/engine/rimport"
 )
 
 const downloadHeader = `// Auto-imported filters by 'gmailctl download'.
diff --git a/cmd/gmailctl/cmd/edit_cmd.go b/cmd/gmailctl/cmd/edit_cmd.go
index 2b6c547..e5b1202 100644
--- a/cmd/gmailctl/cmd/edit_cmd.go
+++ b/cmd/gmailctl/cmd/edit_cmd.go
@@ -10,9 +10,9 @@ import (
 
 	"github.com/spf13/cobra"
 
-	"github.com/mbrt/gmailctl/internal/api"
-	papply "github.com/mbrt/gmailctl/internal/apply"
-	"github.com/mbrt/gmailctl/internal/config"
+	"github.com/mbrt/gmailctl/internal/engine/api"
+	papply "github.com/mbrt/gmailctl/internal/engine/apply"
+	"github.com/mbrt/gmailctl/internal/engine/config"
 	"github.com/mbrt/gmailctl/internal/errors"
 )
 
diff --git a/cmd/gmailctl/cmd/export_cmd.go b/cmd/gmailctl/cmd/export_cmd.go
index 2bb7b0d..9053669 100644
--- a/cmd/gmailctl/cmd/export_cmd.go
+++ b/cmd/gmailctl/cmd/export_cmd.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/spf13/cobra"
 
-	"github.com/mbrt/gmailctl/internal/export/xml"
+	"github.com/mbrt/gmailctl/internal/engine/export/xml"
 )
 
 var (
diff --git a/cmd/gmailctl/cmd/init_cmd.go b/cmd/gmailctl/cmd/init_cmd.go
index 84101f3..16e8e24 100644
--- a/cmd/gmailctl/cmd/init_cmd.go
+++ b/cmd/gmailctl/cmd/init_cmd.go
@@ -6,6 +6,8 @@ import (
 	"path"
 
 	"github.com/spf13/cobra"
+
+	"github.com/mbrt/gmailctl/internal/data"
 )
 
 var initReset bool
@@ -69,11 +71,11 @@ func handleCfgDir() (err error) {
 
 	// Create default config files
 	cfgFile := path.Join(cfgDir, "config.jsonnet")
-	if err := createDefault(cfgFile, DefaultConfig()); err != nil {
+	if err := createDefault(cfgFile, data.DefaultConfig()); err != nil {
 		return err
 	}
 	libFile := path.Join(cfgDir, "gmailctl.libsonnet")
-	return createDefault(libFile, GmailctlLib())
+	return createDefault(libFile, data.GmailctlLib())
 }
 
 func createDefault(path, contents string) (err error) {
diff --git a/cmd/gmailctl/cmd/upstream.go b/cmd/gmailctl/cmd/upstream.go
index 050ca28..7ba804e 100644
--- a/cmd/gmailctl/cmd/upstream.go
+++ b/cmd/gmailctl/cmd/upstream.go
@@ -1,8 +1,8 @@
 package cmd
 
 import (
-	"github.com/mbrt/gmailctl/internal/api"
-	papply "github.com/mbrt/gmailctl/internal/apply"
+	"github.com/mbrt/gmailctl/internal/engine/api"
+	papply "github.com/mbrt/gmailctl/internal/engine/apply"
 )
 
 func upstreamConfig(gmailapi *api.GmailAPI) (papply.GmailConfig, error) {
diff --git a/cmd/gmailctl/localcred/local_provider.go b/cmd/gmailctl/localcred/local_provider.go
index 175a0b2..26cf30f 100644
--- a/cmd/gmailctl/localcred/local_provider.go
+++ b/cmd/gmailctl/localcred/local_provider.go
@@ -9,7 +9,7 @@ import (
 	"google.golang.org/api/gmail/v1"
 
 	"github.com/mbrt/gmailctl/cmd/gmailctl/cmd"
-	"github.com/mbrt/gmailctl/internal/api"
+	"github.com/mbrt/gmailctl/internal/engine/api"
 )
 
 const (
diff --git a/go.mod b/go.mod
index ea5223c..e6f5400 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module github.com/mbrt/gmailctl
 
-go 1.15
+go 1.17
 
 require (
 	github.com/google/go-jsonnet v0.17.0
@@ -9,7 +9,27 @@ require (
 	github.com/pmezard/go-difflib v1.0.0
 	github.com/spf13/cobra v1.2.1
 	github.com/stretchr/testify v1.7.0
-	golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1
+	golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8
 	google.golang.org/api v0.60.0
 	gopkg.in/yaml.v2 v2.4.0
 )
+
+require (
+	cloud.google.com/go v0.97.0 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+	github.com/golang/protobuf v1.5.2 // indirect
+	github.com/googleapis/gax-go/v2 v2.1.1 // indirect
+	github.com/hashicorp/errwrap v1.1.0 // indirect
+	github.com/inconshreveable/mousetrap v1.0.0 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
+	go.opencensus.io v0.23.0 // indirect
+	golang.org/x/net v0.0.0-20211105192438-b53810dc28af // indirect
+	golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68 // indirect
+	golang.org/x/text v0.3.7 // indirect
+	google.golang.org/appengine v1.6.7 // indirect
+	google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247 // indirect
+	google.golang.org/grpc v1.42.0 // indirect
+	google.golang.org/protobuf v1.27.1 // indirect
+	gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
+)
diff --git a/go.sum b/go.sum
index d413630..8cda57f 100644
--- a/go.sum
+++ b/go.sum
@@ -56,6 +56,7 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
 github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
 github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
 github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -63,7 +64,11 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
 github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
@@ -77,6 +82,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
@@ -92,6 +98,8 @@ github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4er
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -171,6 +179,8 @@ github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBt
 github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
 github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
 github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
 github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
 github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
 github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
@@ -360,6 +370,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
 golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 h1:a8jGStKg0XqKDlKqjLrXn0ioF5MH36pT7Z0BRTqLhbk=
 golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211105192438-b53810dc28af h1:SMeNJG/vclJ5wyBBd4xupMsSJIHTd1coW9g7q6KOjmY=
+golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -376,8 +388,9 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ
 golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 h1:B333XXssMuKQeBwiNODx4TupZy7bf4sxFZnN2ZOcvUE=
 golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
+golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -442,6 +455,8 @@ golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
 golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68 h1:Ywe/f3fNleF8I6F6qv3MeFoSZ6CTf2zBMMa/7qVML8M=
+golang.org/x/sys v0.0.0-20211106132015-ebca88c72f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -452,6 +467,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -613,6 +630,8 @@ google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEc
 google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/genproto v0.0.0-20211021150943-2b146023228c h1:FqrtZMB5Wr+/RecOM3uPJNPfWR8Upb5hAPnt7PU6i4k=
 google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
+google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247 h1:ZONpjmFT5e+I/0/xE3XXbG5OIvX2hRYzol04MhKBl2E=
+google.golang.org/genproto v0.0.0-20211104193956-4c6863e31247/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -639,6 +658,8 @@ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
 google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
 google.golang.org/grpc v1.40.0 h1:AGJ0Ih4mHjSeibYkFGh1dD9KJ/eOtZ93I6hoHhukQ5Q=
 google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
+google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A=
+google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
 google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
 google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
diff --git a/hack/check-codegen.sh b/hack/check-codegen.sh
deleted file mode 100755
index e9b0c19..0000000
--- a/hack/check-codegen.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-set -e
-
-# check that we are not committing with things still to generate
-go get github.com/go-bindata/go-bindata/go-bindata
-go generate ./...
-git diff --exit-code -- pkg/ cmd/
diff --git a/hack/move-pkg.sh b/hack/move-pkg.sh
new file mode 100755
index 0000000..3ccdc50
--- /dev/null
+++ b/hack/move-pkg.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# This script is used to move a package from one directory to another.
+
+function move() {
+    local name=$1
+    local src=internal/${name}
+    local dst=internal/engine/${name}
+
+    mv "${src}" "${dst}"
+    for file in $(rg -l "${src}"); do
+        sed -i "s@${src}@${dst}@g" "${file}"
+    done
+}
+
+move api
+move apply
+move cfgtest
+move config
+move export
+move filter
+move gmail
+move graph
+move label
+move parser
+move rimport
diff --git a/internal/data/data.go b/internal/data/data.go
new file mode 100644
index 0000000..e9b24f3
--- /dev/null
+++ b/internal/data/data.go
@@ -0,0 +1,20 @@
+package data
+
+import _ "embed" // required to enable go:embed directives.
+
+var (
+	//go:embed gmailctl.libsonnet
+	gmailctlLib string
+	//go:embed default-config.jsonnet
+	defaultConfig string
+)
+
+// GmailctlLib returns the embedded gmailctl.libsonnet file
+func GmailctlLib() string {
+	return gmailctlLib
+}
+
+// DefaultConfig returns the embedded default configuration file
+func DefaultConfig() string {
+	return defaultConfig
+}
diff --git a/internal/config/testdata/chainfilters.json b/internal/data/testdata/chainfilters.json
similarity index 100%
rename from internal/config/testdata/chainfilters.json
rename to internal/data/testdata/chainfilters.json
diff --git a/internal/config/testdata/chainfilters.jsonnet b/internal/data/testdata/chainfilters.jsonnet
similarity index 100%
rename from internal/config/testdata/chainfilters.jsonnet
rename to internal/data/testdata/chainfilters.jsonnet
diff --git a/internal/config/testdata/gmailctl.libsonnet b/internal/data/testdata/gmailctl.libsonnet
similarity index 100%
rename from internal/config/testdata/gmailctl.libsonnet
rename to internal/data/testdata/gmailctl.libsonnet
diff --git a/internal/api/api.go b/internal/engine/api/api.go
similarity index 96%
rename from internal/api/api.go
rename to internal/engine/api/api.go
index 2828a23..4638d42 100644
--- a/internal/api/api.go
+++ b/internal/engine/api/api.go
@@ -8,9 +8,9 @@ import (
 	"google.golang.org/api/googleapi"
 
 	"github.com/mbrt/gmailctl/internal/errors"
-	exportapi "github.com/mbrt/gmailctl/internal/export/api"
-	"github.com/mbrt/gmailctl/internal/filter"
-	"github.com/mbrt/gmailctl/internal/label"
+	exportapi "github.com/mbrt/gmailctl/internal/engine/export/api"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
+	"github.com/mbrt/gmailctl/internal/engine/label"
 )
 
 const (
diff --git a/internal/api/auth.go b/internal/engine/api/auth.go
similarity index 100%
rename from internal/api/auth.go
rename to internal/engine/api/auth.go
diff --git a/internal/apply/apply.go b/internal/engine/apply/apply.go
similarity index 96%
rename from internal/apply/apply.go
rename to internal/engine/apply/apply.go
index bfa2347..07337ef 100644
--- a/internal/apply/apply.go
+++ b/internal/engine/apply/apply.go
@@ -5,10 +5,10 @@ import (
 	"sort"
 	"strings"
 
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
-	"github.com/mbrt/gmailctl/internal/filter"
-	"github.com/mbrt/gmailctl/internal/label"
-	"github.com/mbrt/gmailctl/internal/parser"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
+	"github.com/mbrt/gmailctl/internal/engine/label"
+	"github.com/mbrt/gmailctl/internal/engine/parser"
 )
 
 // GmailConfig represents a Gmail configuration.
diff --git a/internal/cfgtest/testdata/fail.jsonnet b/internal/engine/cfgtest/testdata/fail.jsonnet
similarity index 100%
rename from internal/cfgtest/testdata/fail.jsonnet
rename to internal/engine/cfgtest/testdata/fail.jsonnet
diff --git a/internal/cfgtest/testdata/invalid.jsonnet b/internal/engine/cfgtest/testdata/invalid.jsonnet
similarity index 100%
rename from internal/cfgtest/testdata/invalid.jsonnet
rename to internal/engine/cfgtest/testdata/invalid.jsonnet
diff --git a/internal/cfgtest/testdata/pass.jsonnet b/internal/engine/cfgtest/testdata/pass.jsonnet
similarity index 100%
rename from internal/cfgtest/testdata/pass.jsonnet
rename to internal/engine/cfgtest/testdata/pass.jsonnet
diff --git a/internal/config/read.go b/internal/engine/config/read.go
similarity index 98%
rename from internal/config/read.go
rename to internal/engine/config/read.go
index e7d388f..68dd0ad 100644
--- a/internal/config/read.go
+++ b/internal/engine/config/read.go
@@ -10,7 +10,7 @@ import (
 
 	"github.com/google/go-jsonnet"
 
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 	"github.com/mbrt/gmailctl/internal/errors"
 )
 
diff --git a/internal/config/v1alpha3/config.go b/internal/engine/config/v1alpha3/config.go
similarity index 98%
rename from internal/config/v1alpha3/config.go
rename to internal/engine/config/v1alpha3/config.go
index 40848f3..e48854d 100644
--- a/internal/config/v1alpha3/config.go
+++ b/internal/engine/config/v1alpha3/config.go
@@ -4,7 +4,7 @@ import (
 	"reflect"
 	"strings"
 
-	"github.com/mbrt/gmailctl/internal/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
 )
 
 // Version is the latest supported version.
diff --git a/internal/export/api/api_export.go b/internal/engine/export/api/api_export.go
similarity index 97%
rename from internal/export/api/api_export.go
rename to internal/engine/export/api/api_export.go
index 1fd6b30..20b795c 100644
--- a/internal/export/api/api_export.go
+++ b/internal/engine/export/api/api_export.go
@@ -6,8 +6,8 @@ import (
 	gmailv1 "google.golang.org/api/gmail/v1"
 
 	"github.com/mbrt/gmailctl/internal/errors"
-	"github.com/mbrt/gmailctl/internal/filter"
-	"github.com/mbrt/gmailctl/internal/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
 	"github.com/mbrt/gmailctl/internal/reporting"
 )
 
diff --git a/internal/export/api/api_import.go b/internal/engine/export/api/api_import.go
similarity index 98%
rename from internal/export/api/api_import.go
rename to internal/engine/export/api/api_import.go
index 85b773e..859184f 100644
--- a/internal/export/api/api_import.go
+++ b/internal/engine/export/api/api_import.go
@@ -9,8 +9,8 @@ import (
 	"github.com/hashicorp/go-multierror"
 	gmailv1 "google.golang.org/api/gmail/v1"
 
-	"github.com/mbrt/gmailctl/internal/filter"
-	"github.com/mbrt/gmailctl/internal/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
 )
 
 var (
diff --git a/internal/export/api/api_test.go b/internal/engine/export/api/api_test.go
similarity index 100%
rename from internal/export/api/api_test.go
rename to internal/engine/export/api/api_test.go
diff --git a/internal/export/api/label.go b/internal/engine/export/api/label.go
similarity index 95%
rename from internal/export/api/label.go
rename to internal/engine/export/api/label.go
index 755a08e..8c333d9 100644
--- a/internal/export/api/label.go
+++ b/internal/engine/export/api/label.go
@@ -1,6 +1,6 @@
 package api
 
-import "github.com/mbrt/gmailctl/internal/label"
+import "github.com/mbrt/gmailctl/internal/engine/label"
 
 const (
 	labelIDInbox     = "INBOX"
diff --git a/internal/export/xml/consts.go b/internal/engine/export/xml/consts.go
similarity index 96%
rename from internal/export/xml/consts.go
rename to internal/engine/export/xml/consts.go
index f820d6d..92d8ba5 100644
--- a/internal/export/xml/consts.go
+++ b/internal/engine/export/xml/consts.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/mbrt/gmailctl/internal/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
 )
 
 // Property values
diff --git a/internal/export/xml/marshal.go b/internal/engine/export/xml/marshal.go
similarity index 97%
rename from internal/export/xml/marshal.go
rename to internal/engine/export/xml/marshal.go
index a3820b9..3718ef8 100644
--- a/internal/export/xml/marshal.go
+++ b/internal/engine/export/xml/marshal.go
@@ -5,8 +5,8 @@ import (
 	"io"
 	"time"
 
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
-	"github.com/mbrt/gmailctl/internal/filter"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
 )
 
 // DefaultExporter returns a default XML exporter.
diff --git a/internal/filter/convert.go b/internal/engine/filter/convert.go
similarity index 99%
rename from internal/filter/convert.go
rename to internal/engine/filter/convert.go
index f64a65b..c55ad3f 100644
--- a/internal/filter/convert.go
+++ b/internal/engine/filter/convert.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/mbrt/gmailctl/internal/parser"
+	"github.com/mbrt/gmailctl/internal/engine/parser"
 )
 
 // There's no documented limit on filter size on Gmail, but this educated guess
diff --git a/internal/filter/filter.go b/internal/engine/filter/filter.go
similarity index 98%
rename from internal/filter/filter.go
rename to internal/engine/filter/filter.go
index 61c5289..ffc585e 100644
--- a/internal/filter/filter.go
+++ b/internal/engine/filter/filter.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/mbrt/gmailctl/internal/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
 )
 
 // Filters is a list of filters created in Gmail.
diff --git a/internal/gmail/consts.go b/internal/engine/gmail/consts.go
similarity index 100%
rename from internal/gmail/consts.go
rename to internal/engine/gmail/consts.go
diff --git a/internal/label/diff.go b/internal/engine/label/diff.go
similarity index 98%
rename from internal/label/diff.go
rename to internal/engine/label/diff.go
index 41ea0cf..04c2924 100644
--- a/internal/label/diff.go
+++ b/internal/engine/label/diff.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/pmezard/go-difflib/difflib"
 
-	"github.com/mbrt/gmailctl/internal/filter"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
 )
 
 // Diff computes the diff between two lists of labels.
diff --git a/internal/label/label.go b/internal/engine/label/label.go
similarity index 97%
rename from internal/label/label.go
rename to internal/engine/label/label.go
index ae91669..7486c6a 100644
--- a/internal/label/label.go
+++ b/internal/engine/label/label.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 )
 
 // Labels is a list of labels.
diff --git a/internal/label/label_test.go b/internal/engine/label/label_test.go
similarity index 100%
rename from internal/label/label_test.go
rename to internal/engine/label/label_test.go
diff --git a/internal/label/testdata/local.added.diff b/internal/engine/label/testdata/local.added.diff
similarity index 100%
rename from internal/label/testdata/local.added.diff
rename to internal/engine/label/testdata/local.added.diff
diff --git a/internal/label/testdata/local.added.json b/internal/engine/label/testdata/local.added.json
similarity index 100%
rename from internal/label/testdata/local.added.json
rename to internal/engine/label/testdata/local.added.json
diff --git a/internal/label/testdata/local.deleted.diff b/internal/engine/label/testdata/local.deleted.diff
similarity index 100%
rename from internal/label/testdata/local.deleted.diff
rename to internal/engine/label/testdata/local.deleted.diff
diff --git a/internal/label/testdata/local.deleted.json b/internal/engine/label/testdata/local.deleted.json
similarity index 100%
rename from internal/label/testdata/local.deleted.json
rename to internal/engine/label/testdata/local.deleted.json
diff --git a/internal/label/testdata/local.modified.diff b/internal/engine/label/testdata/local.modified.diff
similarity index 100%
rename from internal/label/testdata/local.modified.diff
rename to internal/engine/label/testdata/local.modified.diff
diff --git a/internal/label/testdata/local.modified.json b/internal/engine/label/testdata/local.modified.json
similarity index 100%
rename from internal/label/testdata/local.modified.json
rename to internal/engine/label/testdata/local.modified.json
diff --git a/internal/label/testdata/local.nodiff.diff b/internal/engine/label/testdata/local.nodiff.diff
similarity index 100%
rename from internal/label/testdata/local.nodiff.diff
rename to internal/engine/label/testdata/local.nodiff.diff
diff --git a/internal/label/testdata/local.nodiff.json b/internal/engine/label/testdata/local.nodiff.json
similarity index 100%
rename from internal/label/testdata/local.nodiff.json
rename to internal/engine/label/testdata/local.nodiff.json
diff --git a/internal/label/testdata/upstream.json b/internal/engine/label/testdata/upstream.json
similarity index 100%
rename from internal/label/testdata/upstream.json
rename to internal/engine/label/testdata/upstream.json
diff --git a/internal/parser/ast.go b/internal/engine/parser/ast.go
similarity index 100%
rename from internal/parser/ast.go
rename to internal/engine/parser/ast.go
diff --git a/internal/parser/ast_test.go b/internal/engine/parser/ast_test.go
similarity index 100%
rename from internal/parser/ast_test.go
rename to internal/engine/parser/ast_test.go
diff --git a/internal/parser/lib_test.go b/internal/engine/parser/lib_test.go
similarity index 100%
rename from internal/parser/lib_test.go
rename to internal/engine/parser/lib_test.go
diff --git a/internal/parser/parser.go b/internal/engine/parser/parser.go
similarity index 98%
rename from internal/parser/parser.go
rename to internal/engine/parser/parser.go
index e2b2c12..86d6e22 100644
--- a/internal/parser/parser.go
+++ b/internal/engine/parser/parser.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"strings"
 
-	cfg "github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	cfg "github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 	"github.com/mbrt/gmailctl/internal/errors"
 	"github.com/mbrt/gmailctl/internal/reporting"
 )
diff --git a/internal/parser/testdata/example.jsonnet b/internal/engine/parser/testdata/example.jsonnet
similarity index 100%
rename from internal/parser/testdata/example.jsonnet
rename to internal/engine/parser/testdata/example.jsonnet
diff --git a/internal/rimport/jsonnet.go b/internal/engine/rimport/jsonnet.go
similarity index 100%
rename from internal/rimport/jsonnet.go
rename to internal/engine/rimport/jsonnet.go
diff --git a/internal/rimport/rimport.go b/internal/engine/rimport/rimport.go
similarity index 95%
rename from internal/rimport/rimport.go
rename to internal/engine/rimport/rimport.go
index 0934830..3ebed25 100644
--- a/internal/rimport/rimport.go
+++ b/internal/engine/rimport/rimport.go
@@ -4,10 +4,10 @@ import (
 	"fmt"
 	"strings"
 
-	config "github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	config "github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 	"github.com/mbrt/gmailctl/internal/errors"
-	"github.com/mbrt/gmailctl/internal/filter"
-	"github.com/mbrt/gmailctl/internal/label"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
+	"github.com/mbrt/gmailctl/internal/engine/label"
 	"github.com/mbrt/gmailctl/internal/reporting"
 )
 


Test patch:
diff --git a/cmd/gmailctl-config-migrate/v1alpha3/import_test.go b/cmd/gmailctl-config-migrate/v1alpha3/import_test.go
index 9274851..5b2b1c2 100644
--- a/cmd/gmailctl-config-migrate/v1alpha3/import_test.go
+++ b/cmd/gmailctl-config-migrate/v1alpha3/import_test.go
@@ -12,7 +12,7 @@ import (
 	"gopkg.in/yaml.v2"
 
 	"github.com/mbrt/gmailctl/cmd/gmailctl-config-migrate/v1alpha2"
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 )
 
 func read(path string) []byte {
diff --git a/integration_test.go b/integration_test.go
index 02fc606..fddef1d 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -16,13 +16,13 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"github.com/mbrt/gmailctl/internal/api"
-	"github.com/mbrt/gmailctl/internal/apply"
-	"github.com/mbrt/gmailctl/internal/config"
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
-	"github.com/mbrt/gmailctl/internal/export/xml"
+	"github.com/mbrt/gmailctl/internal/engine/api"
+	"github.com/mbrt/gmailctl/internal/engine/apply"
+	"github.com/mbrt/gmailctl/internal/engine/config"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/export/xml"
 	"github.com/mbrt/gmailctl/internal/fakegmail"
-	"github.com/mbrt/gmailctl/internal/rimport"
+	"github.com/mbrt/gmailctl/internal/engine/rimport"
 )
 
 // update is useful to regenerate the golden files
diff --git a/default-config.jsonnet b/internal/data/default-config.jsonnet
similarity index 100%
rename from default-config.jsonnet
rename to internal/data/default-config.jsonnet
diff --git a/gmailctl.libsonnet b/internal/data/gmailctl.libsonnet
similarity index 100%
rename from gmailctl.libsonnet
rename to internal/data/gmailctl.libsonnet
diff --git a/internal/config/lib_test.go b/internal/data/lib_test.go
similarity index 90%
rename from internal/config/lib_test.go
rename to internal/data/lib_test.go
index 10856cb..93818ba 100644
--- a/internal/config/lib_test.go
+++ b/internal/data/lib_test.go
@@ -1,4 +1,4 @@
-package config
+package data
 
 import (
 	"encoding/json"
@@ -10,7 +10,8 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/config"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 )
 
 // update is useful to regenerate the json files, whenever necessary.
@@ -57,7 +58,7 @@ func TestJsonnetLib(t *testing.T) {
 		jfile := tps.jsonnets[i]
 
 		t.Run(jfile, func(t *testing.T) {
-			jnparsed, err := ReadFile(jfile, "")
+			jnparsed, err := config.ReadFile(jfile, "")
 			assert.Nil(t, err)
 
 			jsfile := tps.jsons[i]
diff --git a/internal/cfgtest/builder.go b/internal/engine/cfgtest/builder.go
similarity index 98%
rename from internal/cfgtest/builder.go
rename to internal/engine/cfgtest/builder.go
index a82f41c..8821d48 100644
--- a/internal/cfgtest/builder.go
+++ b/internal/engine/cfgtest/builder.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/mbrt/gmailctl/internal/parser"
+	"github.com/mbrt/gmailctl/internal/engine/parser"
 )
 
 // NewEvaluator creates a RuleEvaluator starting from a parser criteria.
diff --git a/internal/cfgtest/cfgtest.go b/internal/engine/cfgtest/cfgtest.go
similarity index 93%
rename from internal/cfgtest/cfgtest.go
rename to internal/engine/cfgtest/cfgtest.go
index 06d9356..f564c60 100644
--- a/internal/cfgtest/cfgtest.go
+++ b/internal/engine/cfgtest/cfgtest.go
@@ -6,10 +6,10 @@ import (
 
 	"github.com/pmezard/go-difflib/difflib"
 
-	cfg "github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 	"github.com/mbrt/gmailctl/internal/errors"
-	"github.com/mbrt/gmailctl/internal/gmail"
-	"github.com/mbrt/gmailctl/internal/parser"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/parser"
 	"github.com/mbrt/gmailctl/internal/reporting"
 )
 
@@ -47,7 +47,7 @@ type Rules []Rule
 // ExecTests evaluates all the rules against the given tests.
 //
 // The evaluation stops at the first failing test.
-func (rs Rules) ExecTests(ts []cfg.Test) error {
+func (rs Rules) ExecTests(ts []v1alpha3.Test) error {
 	for i, t := range ts {
 		if err := rs.ExecTest(t); err != nil {
 			name := t.Name
@@ -63,7 +63,7 @@ func (rs Rules) ExecTests(ts []cfg.Test) error {
 // ExecTest evaluates the rules on all the messages of the given test.
 //
 // If the rules apply as expected by the test, no error is returned.
-func (rs Rules) ExecTest(t cfg.Test) error {
+func (rs Rules) ExecTest(t v1alpha3.Test) error {
 	for i, msg := range t.Messages {
 		expected, err := rs.MatchingActions(msg)
 		if err != nil {
@@ -105,7 +105,7 @@ func (rs Rules) ExecTest(t cfg.Test) error {
 // that in Gmail this wouldn't be an error, but a nondeterministic action would be
 // applied. Since this situation is most likely a mistake by the user, we treat it
 // as an error.
-func (rs Rules) MatchingActions(msg cfg.Message) (Actions, error) {
+func (rs Rules) MatchingActions(msg v1alpha3.Message) (Actions, error) {
 	var (
 		res Actions
 		err error
@@ -235,6 +235,6 @@ func stringSliceEqual(s1, s2 []string) bool {
 	return true
 }
 
-func messageDetails(msg cfg.Message) string {
+func messageDetails(msg v1alpha3.Message) string {
 	return fmt.Sprintf("Message: %s", reporting.Prettify(msg, false))
 }
diff --git a/internal/cfgtest/eval.go b/internal/engine/cfgtest/eval.go
similarity index 97%
rename from internal/cfgtest/eval.go
rename to internal/engine/cfgtest/eval.go
index fa17ba6..d4565a4 100644
--- a/internal/cfgtest/eval.go
+++ b/internal/engine/cfgtest/eval.go
@@ -3,7 +3,7 @@ package cfgtest
 import (
 	"strings"
 
-	cfg "github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	cfg "github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 )
 
 type matchType int
diff --git a/internal/cfgtest/eval_test.go b/internal/engine/cfgtest/eval_test.go
similarity index 93%
rename from internal/cfgtest/eval_test.go
rename to internal/engine/cfgtest/eval_test.go
index caba2a1..6744d1e 100644
--- a/internal/cfgtest/eval_test.go
+++ b/internal/engine/cfgtest/eval_test.go
@@ -5,8 +5,8 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	cfg "github.com/mbrt/gmailctl/internal/config/v1alpha3"
-	"github.com/mbrt/gmailctl/internal/parser"
+	cfg "github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/parser"
 )
 
 func TestParseEval(t *testing.T) {
diff --git a/internal/cfgtest/exec_test.go b/internal/engine/cfgtest/exec_test.go
similarity index 84%
rename from internal/cfgtest/exec_test.go
rename to internal/engine/cfgtest/exec_test.go
index 928f49c..9969e90 100644
--- a/internal/cfgtest/exec_test.go
+++ b/internal/engine/cfgtest/exec_test.go
@@ -6,9 +6,9 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/mbrt/gmailctl/internal/apply"
-	"github.com/mbrt/gmailctl/internal/config"
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/apply"
+	"github.com/mbrt/gmailctl/internal/engine/config"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 )
 
 func readConfig(t *testing.T, path string) v1alpha3.Config {
diff --git a/internal/cfgtest/lib_test.go b/internal/engine/cfgtest/lib_test.go
similarity index 91%
rename from internal/cfgtest/lib_test.go
rename to internal/engine/cfgtest/lib_test.go
index e432aa0..7ac82ff 100644
--- a/internal/cfgtest/lib_test.go
+++ b/internal/engine/cfgtest/lib_test.go
@@ -1,6 +1,6 @@
 package cfgtest
 
-import "github.com/mbrt/gmailctl/internal/parser"
+import "github.com/mbrt/gmailctl/internal/engine/parser"
 
 func and(children ...parser.CriteriaAST) *parser.Node {
 	return &parser.Node{
diff --git a/internal/export/api/api_export_test.go b/internal/engine/export/api/api_export_test.go
similarity index 95%
rename from internal/export/api/api_export_test.go
rename to internal/engine/export/api/api_export_test.go
index 943fc60..5ebe449 100644
--- a/internal/export/api/api_export_test.go
+++ b/internal/engine/export/api/api_export_test.go
@@ -6,9 +6,9 @@ import (
 	"github.com/stretchr/testify/assert"
 	gmailv1 "google.golang.org/api/gmail/v1"
 
-	"github.com/mbrt/gmailctl/internal/filter"
-	"github.com/mbrt/gmailctl/internal/gmail"
-	"github.com/mbrt/gmailctl/internal/label"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/label"
 )
 
 func emptyLabelMap() LabelMap {
diff --git a/internal/export/api/api_import_test.go b/internal/engine/export/api/api_import_test.go
similarity index 96%
rename from internal/export/api/api_import_test.go
rename to internal/engine/export/api/api_import_test.go
index 3144a27..c482dc2 100644
--- a/internal/export/api/api_import_test.go
+++ b/internal/engine/export/api/api_import_test.go
@@ -6,9 +6,9 @@ import (
 	"github.com/stretchr/testify/assert"
 	gmailv1 "google.golang.org/api/gmail/v1"
 
-	"github.com/mbrt/gmailctl/internal/filter"
-	"github.com/mbrt/gmailctl/internal/gmail"
-	"github.com/mbrt/gmailctl/internal/label"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/label"
 )
 
 func TestImportActions(t *testing.T) {
diff --git a/internal/export/xml/marshal_test.go b/internal/engine/export/xml/marshal_test.go
similarity index 96%
rename from internal/export/xml/marshal_test.go
rename to internal/engine/export/xml/marshal_test.go
index c8e4409..d4b16d1 100644
--- a/internal/export/xml/marshal_test.go
+++ b/internal/engine/export/xml/marshal_test.go
@@ -8,9 +8,9 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
-	"github.com/mbrt/gmailctl/internal/filter"
-	"github.com/mbrt/gmailctl/internal/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
 )
 
 func testNow() time.Time {
diff --git a/internal/filter/convert_test.go b/internal/engine/filter/convert_test.go
similarity index 98%
rename from internal/filter/convert_test.go
rename to internal/engine/filter/convert_test.go
index 12678d7..773359a 100644
--- a/internal/filter/convert_test.go
+++ b/internal/engine/filter/convert_test.go
@@ -5,8 +5,8 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/mbrt/gmailctl/internal/gmail"
-	"github.com/mbrt/gmailctl/internal/parser"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/parser"
 )
 
 func boolptr(a bool) *bool {
diff --git a/internal/filter/diff.go b/internal/engine/filter/diff.go
similarity index 100%
rename from internal/filter/diff.go
rename to internal/engine/filter/diff.go
diff --git a/internal/filter/diff_test.go b/internal/engine/filter/diff_test.go
similarity index 99%
rename from internal/filter/diff_test.go
rename to internal/engine/filter/diff_test.go
index a4d32f8..d7fc0f5 100644
--- a/internal/filter/diff_test.go
+++ b/internal/engine/filter/diff_test.go
@@ -6,7 +6,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/mbrt/gmailctl/internal/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
 )
 
 func TestNoDiff(t *testing.T) {
diff --git a/internal/label/diff_test.go b/internal/engine/label/diff_test.go
similarity index 97%
rename from internal/label/diff_test.go
rename to internal/engine/label/diff_test.go
index a021049..6c0726b 100644
--- a/internal/label/diff_test.go
+++ b/internal/engine/label/diff_test.go
@@ -11,7 +11,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/mbrt/gmailctl/internal/filter"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
 )
 
 // update is useful to regenerate the diff files, whenever necessary.
diff --git a/internal/parser/parser_test.go b/internal/engine/parser/parser_test.go
similarity index 92%
rename from internal/parser/parser_test.go
rename to internal/engine/parser/parser_test.go
index 3053fc3..981a9c8 100644
--- a/internal/parser/parser_test.go
+++ b/internal/engine/parser/parser_test.go
@@ -5,8 +5,8 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/mbrt/gmailctl/internal/config"
-	"github.com/mbrt/gmailctl/internal/config/v1alpha3"
+	"github.com/mbrt/gmailctl/internal/engine/config"
+	"github.com/mbrt/gmailctl/internal/engine/config/v1alpha3"
 )
 
 func readConfig(t *testing.T, path string) v1alpha3.Config {
diff --git a/internal/rimport/rimport_test.go b/internal/engine/rimport/rimport_test.go
similarity index 95%
rename from internal/rimport/rimport_test.go
rename to internal/engine/rimport/rimport_test.go
index 58d331d..e71abcd 100644
--- a/internal/rimport/rimport_test.go
+++ b/internal/engine/rimport/rimport_test.go
@@ -8,7 +8,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/mbrt/gmailctl/internal/filter"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
 )
 
 // update is useful to regenerate the json files, whenever necessary.
diff --git a/internal/rimport/testdata/config.json b/internal/engine/rimport/testdata/config.json
similarity index 100%
rename from internal/rimport/testdata/config.json
rename to internal/engine/rimport/testdata/config.json
diff --git a/internal/rimport/testdata/remote.json b/internal/engine/rimport/testdata/remote.json
similarity index 100%
rename from internal/rimport/testdata/remote.json
rename to internal/engine/rimport/testdata/remote.json
diff --git a/internal/fakegmail/fakegmail_test.go b/internal/fakegmail/fakegmail_test.go
index f94b181..c6541f8 100644
--- a/internal/fakegmail/fakegmail_test.go
+++ b/internal/fakegmail/fakegmail_test.go
@@ -6,11 +6,11 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"github.com/mbrt/gmailctl/internal/api"
+	"github.com/mbrt/gmailctl/internal/engine/api"
 	"github.com/mbrt/gmailctl/internal/fakegmail"
-	"github.com/mbrt/gmailctl/internal/filter"
-	"github.com/mbrt/gmailctl/internal/gmail"
-	"github.com/mbrt/gmailctl/internal/label"
+	"github.com/mbrt/gmailctl/internal/engine/filter"
+	"github.com/mbrt/gmailctl/internal/engine/gmail"
+	"github.com/mbrt/gmailctl/internal/engine/label"
 )
 
 func TestLabels(t *testing.T) {