Repository: Shopify/themekit
Pull request #756 (2020-08-19 18:20:24)

Prepare release for 1.1.0-beta
Waiting for merges of:

- [x] #745
- [x] #755

### Warn Checklist
- [x] This changes the interface and requires a Major/Minor version change.
- [x] I have :tophat:'d these changes by using the commands I changed by hand.
- [ ] I have added a dependancy to the project.



Patch:
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index c204617..98440cd 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -61,7 +61,7 @@ A man in the middle proxy is the easiest way to introspect the requests that the
 
 # Deploying Themekit
 
-- Update ThemeKitVersion in `src/release/version.go` and commit.
+- Update ThemeKitVersion in `src/release/release.go` and commit.
 - Make the release tool `go install ./cmd/tkrelease`
 - run `git tag <version> && git push origin --tags && git push`
 - Update the changelog.txt with the date of the version release
diff --git a/changelog.txt b/changelog.txt
index 833614f..46d661c 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,10 @@
+v1.1.0-beta (Aug 19 2020)
+====================
+- Use checksums to prevent unnecessary uploads and downloads for unchanged assets
+- Added Strict theme ID handling to prevent accidental live theme alterations (#739)
+- Use checksums sourced from API rather than local filesystem in file watcher to optimize startup and fix changes made
+while the watch command was not running
+
 v1.0.3 (Dec 17 2019)
 ====================
 - Add support for pages/customers directory
diff --git a/src/release/release.go b/src/release/release.go
index f66f46a..3e6d19d 100644
--- a/src/release/release.go
+++ b/src/release/release.go
@@ -30,7 +30,7 @@ var (
 		"windows-amd64": "theme.exe",
 	}
 	// ThemeKitVersion is the version build of the library
-	ThemeKitVersion, _ = version.NewVersion("1.0.3")
+	ThemeKitVersion, _ = version.NewVersion("1.1.0-beta")
 )
 
 const (
diff --git a/src/shopify/theme_client.go b/src/shopify/theme_client.go
index c3eb685..f37b3f6 100644
--- a/src/shopify/theme_client.go
+++ b/src/shopify/theme_client.go
@@ -17,9 +17,6 @@ import (
 	"github.com/Shopify/themekit/src/httpify"
 )
 
-// APIPath is the version of the Admin REST API to use
-const APIPath = "/admin/api/unstable/"
-
 var (
 	// ErrCriticalFile will be returned when trying to remove a critical file
 	ErrCriticalFile = errors.New("this file is critical and removing it would cause your theme to become non-functional")
@@ -146,7 +143,7 @@ func (c Client) GetShop() (Shop, error) {
 
 // Themes will return all the available themes on a domain.
 func (c Client) Themes() ([]Theme, error) {
-	resp, err := c.http.Get(APIPath + "themes.json", nil)
+	resp, err := c.http.Get("/admin/themes.json", nil)
 	if err != nil {
 		return []Theme{}, err
 	}
@@ -166,7 +163,7 @@ func (c *Client) CreateNewTheme(name string) (theme Theme, err error) {
 		return Theme{}, ErrThemeNameRequired
 	}
 
-	resp, err := c.http.Post(APIPath + "themes.json", map[string]interface{}{"theme": Theme{Name: name}}, nil)
+	resp, err := c.http.Post("/admin/themes.json", map[string]interface{}{"theme": Theme{Name: name}}, nil)
 	if err != nil {
 		return Theme{}, err
 	}
@@ -190,7 +187,7 @@ func (c Client) GetInfo() (Theme, error) {
 		return Theme{}, ErrInfoWithoutThemeID
 	}
 
-	resp, err := c.http.Get(fmt.Sprintf(APIPath + "themes/%s.json", c.themeID), nil)
+	resp, err := c.http.Get(fmt.Sprintf("/admin/themes/%s.json", c.themeID), nil)
 	if err != nil {
 		return Theme{}, err
 	} else if resp.StatusCode == 404 {
@@ -212,7 +209,7 @@ func (c Client) PublishTheme() error {
 	}
 
 	resp, err := c.http.Put(
-		fmt.Sprintf(APIPath + "themes/%s.json", c.themeID),
+		fmt.Sprintf("/admin/themes/%s.json", c.themeID),
 		map[string]Theme{"theme": {Role: "main"}},
 		nil,
 	)
@@ -345,9 +342,9 @@ func (c Client) DeleteAsset(asset Asset) error {
 }
 
 func (c Client) assetPath(query map[string]string) string {
-	formatted := APIPath + "assets.json"
+	formatted := "/admin/assets.json"
 	if c.themeID != "" {
-		formatted = fmt.Sprintf(APIPath + "themes/%s/assets.json", c.themeID)
+		formatted = fmt.Sprintf("/admin/themes/%s/assets.json", c.themeID)
 	}
 
 	if len(query) > 0 {


Test patch:
diff --git a/src/shopify/theme_client_test.go b/src/shopify/theme_client_test.go
index 5fd07de..a52de06 100644
--- a/src/shopify/theme_client_test.go
+++ b/src/shopify/theme_client_test.go
@@ -16,8 +16,6 @@ import (
 
 var NoHeaders = map[string]string(nil)
 
-// APIPath is the version of the Admin REST API to use
-
 func TestNewThemeClient(t *testing.T) {
 	testcases := []struct {
 		e   *env.Env
@@ -94,7 +92,7 @@ func TestThemeClient_Themes(t *testing.T) {
 		client, _ := NewClient(&env.Env{})
 		client.http = m
 
-		expectation := m.On("Get", APIPath + "themes.json", NoHeaders)
+		expectation := m.On("Get", "/admin/themes.json", NoHeaders)
 		if testcase.resperr != "" {
 			expectation.Return(nil, errors.New(testcase.resperr))
 		} else {
@@ -135,9 +133,9 @@ func TestThemeClient_CreateNewTheme(t *testing.T) {
 		query := map[string]interface{}{"theme": Theme{Name: testcase.in}}
 
 		if testcase.resp != "" {
-			m.On("Post", APIPath + "themes.json", query, NoHeaders).Return(jsonResponse(testcase.resp, 200), nil)
+			m.On("Post", "/admin/themes.json", query, NoHeaders).Return(jsonResponse(testcase.resp, 200), nil)
 		} else if testcase.resperr != "" {
-			m.On("Post", APIPath + "themes.json", query, NoHeaders).Return(nil, errors.New(testcase.resperr))
+			m.On("Post", "/admin/themes.json", query, NoHeaders).Return(nil, errors.New(testcase.resperr))
 		}
 
 		theme, err := client.CreateNewTheme(testcase.in)
@@ -173,7 +171,7 @@ func TestThemeClient_GetInfo(t *testing.T) {
 		client, _ := NewClient(&env.Env{ThemeID: testcase.themeID})
 		client.http = m
 
-		expectation := m.On("Get", fmt.Sprintf(APIPath + "themes/%s.json", testcase.themeID), NoHeaders)
+		expectation := m.On("Get", fmt.Sprintf("/admin/themes/%s.json", testcase.themeID), NoHeaders)
 		if testcase.resperr != "" {
 			expectation.Return(nil, errors.New(testcase.resperr))
 		} else {
@@ -216,7 +214,7 @@ func TestThemeClient_PublishTheme(t *testing.T) {
 
 		expectation := m.On(
 			"Put",
-			fmt.Sprintf(APIPath + "themes/%s.json", testcase.themeID),
+			fmt.Sprintf("/admin/themes/%s.json", testcase.themeID),
 			map[string]Theme{"theme": {Role: "main"}},
 			NoHeaders,
 		)
@@ -256,7 +254,7 @@ func TestThemeClient_GetAllAssets(t *testing.T) {
 		client, _ := NewClient(&env.Env{ThemeID: "123"})
 		client.http = m
 
-		expectation := m.On("Get", APIPath + "themes/123/assets.json?fields=key%2Cchecksum", NoHeaders)
+		expectation := m.On("Get", "/admin/themes/123/assets.json?fields=key%2Cchecksum", NoHeaders)
 		if testcase.resperr != "" {
 			expectation.Return(nil, errors.New(testcase.resperr))
 		} else {
@@ -299,7 +297,7 @@ func TestThemeClient_GetAllAssets(t *testing.T) {
 		m := new(mocks.HttpAdapter)
 		client, _ := NewClient(&env.Env{ThemeID: "123", IgnoredFiles: testcase.ignore})
 		client.http = m
-		m.On("Get", APIPath + "themes/123/assets.json?fields=key%2Cchecksum", NoHeaders).Return(jsonResponse(testcase.input, 200), nil)
+		m.On("Get", "/admin/themes/123/assets.json?fields=key%2Cchecksum", NoHeaders).Return(jsonResponse(testcase.input, 200), nil)
 		assets, err := client.GetAllAssets()
 		assert.Nil(t, err)
 		assert.Equal(t, testcase.expected, assets)
@@ -322,7 +320,7 @@ func TestThemeClient_GetAsset(t *testing.T) {
 		client, _ := NewClient(&env.Env{ThemeID: "123"})
 		client.http = m
 
-		expectation := m.On("Get", APIPath + "themes/123/assets.json?asset%5Bkey%5D=filename.txt", NoHeaders)
+		expectation := m.On("Get", "/admin/themes/123/assets.json?asset%5Bkey%5D=filename.txt", NoHeaders)
 		if testcase.resperr != "" {
 			expectation.Return(nil, errors.New(testcase.resperr))
 		} else if testcase.code != 0 {
@@ -358,7 +356,7 @@ func TestThemeClient_UpdateAsset(t *testing.T) {
 		client, _ := NewClient(&env.Env{ThemeID: "123"})
 		client.http = m
 
-		expectation := m.On("Put", APIPath + "themes/123/assets.json", map[string]Asset{"asset": {Key: "filename.txt"}}, NoHeaders)
+		expectation := m.On("Put", "/admin/themes/123/assets.json", map[string]Asset{"asset": {Key: "filename.txt"}}, NoHeaders)
 		if testcase.resperr != "" {
 			expectation.Return(nil, errors.New(testcase.resperr))
 		} else if testcase.code != 0 {
@@ -400,13 +398,13 @@ func TestThemeClient_UpdateAsset(t *testing.T) {
 
 	m.On(
 		"Delete",
-		APIPath + "themes/123/assets.json?asset%5Bkey%5D=filename.txt.liquid",
+		"/admin/themes/123/assets.json?asset%5Bkey%5D=filename.txt.liquid",
 		NoHeaders,
 	).Return(jsonResponse("{}", 200), nil)
 
 	m.On(
 		"Put",
-		APIPath + "themes/123/assets.json",
+		"/admin/themes/123/assets.json",
 		map[string]Asset{"asset": asset},
 		NoHeaders,
 	).Return(jsonResponse(`{"asset":{"key":"assets/hello.txt"}}`, 200), nil)
@@ -431,7 +429,7 @@ func TestThemeClient_DeleteAsset(t *testing.T) {
 		client, _ := NewClient(&env.Env{ThemeID: "123"})
 		client.http = m
 
-		expectation := m.On("Delete", APIPath + "themes/123/assets.json?asset%5Bkey%5D=filename.txt", NoHeaders)
+		expectation := m.On("Delete", "/admin/themes/123/assets.json?asset%5Bkey%5D=filename.txt", NoHeaders)
 		if testcase.resperr != "" {
 			expectation.Return(nil, errors.New(testcase.resperr))
 		} else {
@@ -455,10 +453,10 @@ func TestThemeClient_assetPath(t *testing.T) {
 		query         map[string]string
 		themeID, path string
 	}{
-		{themeID: "123", path: APIPath + "themes/123/assets.json?asset%5Bkey%5D=layout%2Ftheme.liquid", query: map[string]string{"asset[key]": "layout/theme.liquid"}},
-		{path: "/admin/api/unstable/assets.json?asset%5Bkey%5D=layout%2Ftheme.liquid", query: map[string]string{"asset[key]": "layout/theme.liquid"}},
-		{themeID: "123", path: APIPath + "themes/123/assets.json"},
-		{path: "/admin/api/unstable/assets.json"},
+		{themeID: "123", path: "/admin/themes/123/assets.json?asset%5Bkey%5D=layout%2Ftheme.liquid", query: map[string]string{"asset[key]": "layout/theme.liquid"}},
+		{path: "/admin/assets.json?asset%5Bkey%5D=layout%2Ftheme.liquid", query: map[string]string{"asset[key]": "layout/theme.liquid"}},
+		{themeID: "123", path: "/admin/themes/123/assets.json"},
+		{path: "/admin/assets.json"},
 	}
 
 	for _, testcase := range testcases {