Skip to content
This repository was archived by the owner on Mar 24, 2023. It is now read-only.

Commit ec7bec1

Browse files
authored
Merge pull request #654 from dexhorthy/wip-setgithub
mechanism for setting github contents during local dev
2 parents 9bda6b1 + 8de73ca commit ec7bec1

File tree

12 files changed

+315
-31
lines changed

12 files changed

+315
-31
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
assets:
2+
v1:
3+
- github:
4+
repo: replicatedhq/test-charts
5+
ref: master
6+
path: /release.yml
7+
dest: ./
8+
lifecycle:
9+
v1:
10+
- render: {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"v1": {
3+
"config": {
4+
"test_option": "abc123_test-option-value"
5+
},
6+
"contentSHA": "b9b0e107bb87dd3843d1d1178a6e9109d42d5e86ac8885a172f23c291b62d3b3",
7+
"metadata": {
8+
"applicationType": "replicated.app",
9+
"customerID": "-Am-_6i5pw0u4AbspOwKN4lZUCn49u_G",
10+
"installationID": "RULNveQLrLj4GDum1yQhvKAcABh0GcLY",
11+
"releaseNotes": "",
12+
"version": "0.0.4"
13+
},
14+
"releaseName": "ship"
15+
}
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
assets:
2+
v1:
3+
- github:
4+
repo: replicatedhq/test-charts
5+
ref: master
6+
path: /release.yml
7+
dest: ./
8+
lifecycle:
9+
v1:
10+
- render: {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
assets:
2+
v1:
3+
- github:
4+
repo: replicatedhq/test-charts
5+
ref: master
6+
path: /release.yml
7+
dest: ./
8+
lifecycle:
9+
v1:
10+
- render: {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"v1": {
3+
"config": {
4+
"test_option": "abc123_test-option-value"
5+
}
6+
}
7+
}

integration/base/github/metadata.yaml

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
customer_id: "-Am-_6i5pw0u4AbspOwKN4lZUCn49u_G"
2+
installation_id: "RULNveQLrLj4GDum1yQhvKAcABh0GcLY"
3+
release_version: "0.0.4"
4+
set_github_contents: "replicatedhq/test-charts:/release.yml:master:.ship"
5+
disable_online: true

integration/base/integration_test.go

+9-7
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ import (
1919
)
2020

2121
type TestMetadata struct {
22-
CustomerID string `yaml:"customer_id"`
23-
InstallationID string `yaml:"installation_id"`
24-
ReleaseVersion string `yaml:"release_version"`
25-
SetChannelName string `yaml:"set_channel_name"`
26-
Flavor string `yaml:"flavor"`
27-
DisableOnline bool `yaml:"disable_online"`
28-
NoStateFile bool `yaml:"no_state_file"` // used to denote that there is no input state.json
22+
CustomerID string `yaml:"customer_id"`
23+
InstallationID string `yaml:"installation_id"`
24+
ReleaseVersion string `yaml:"release_version"`
25+
SetChannelName string `yaml:"set_channel_name"`
26+
SetGitHubContents string `yaml:"set_github_contents"`
27+
DisableOnline bool `yaml:"disable_online"`
28+
NoStateFile bool `yaml:"no_state_file"` // used to denote that there is no input state.json
2929
//debugging
3030
SkipCleanup bool `yaml:"skip_cleanup"`
3131
}
@@ -93,12 +93,14 @@ var _ = Describe("ship app", func() {
9393
fmt.Sprintf("--installation-id=%s", testMetadata.InstallationID),
9494
fmt.Sprintf("--set-channel-name=%s", testMetadata.SetChannelName),
9595
fmt.Sprintf("--release-semver=%s", testMetadata.ReleaseVersion),
96+
fmt.Sprintf("--set-github-contents=%s", testMetadata.SetGitHubContents),
9697
"--log-level=off",
9798
"--terraform-yes",
9899
}
99100
if !testMetadata.NoStateFile {
100101
args = append(args, fmt.Sprintf("--state-file=%s", path.Join(testInputPath, ".ship/state.json")))
101102
}
103+
102104
cmd.SetArgs(args)
103105
err := cmd.Execute()
104106
Expect(err).NotTo(HaveOccurred())

pkg/cli/app.go

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package cli
22

33
import (
44
"context"
5+
"fmt"
56
"strings"
67

78
"github.com/replicatedhq/ship/pkg/ship"
@@ -43,6 +44,7 @@ func App() *cobra.Command {
4344
cmd.Flags().String("runbook", "", developerFlagUsage)
4445
cmd.Flags().String("set-channel-name", "", developerFlagUsage)
4546
cmd.Flags().String("set-channel-icon", "", developerFlagUsage)
47+
cmd.Flags().String("set-github-contents", "", fmt.Sprintf("Specify a REPO:REPO_PATH:REF:LOCAL_PATH to override github checkouts to use a local path on the filesystem. %s. ", developerFlagUsage))
4648

4749
// Deprecated developer flags
4850
cmd.Flags().String("studio-file", "", developerFlagUsage)

pkg/lifecycle/render/github/render.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func (r *LocalRenderer) Execute(
8282

8383
if len(files) == 0 {
8484
level.Info(r.Logger).Log("msg", "no files for asset", "repo", asset.Repo, "path", asset.Path)
85-
return nil
85+
return errors.New("github asset returned no files")
8686
}
8787

8888
builder, err := r.BuilderBuilder.FullBuilder(meta, configGroups, templateContext)

pkg/specs/replicatedapp/local.go

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// local.go has methods for resolving a local ship.yaml file, and patching in api.Release info
2+
// that would usually be returned by pg.replicated.com
3+
package replicatedapp
4+
5+
import (
6+
"bytes"
7+
"crypto/sha256"
8+
"encoding/base64"
9+
"fmt"
10+
"os"
11+
"strings"
12+
13+
"github.com/go-kit/kit/log"
14+
"github.com/go-kit/kit/log/level"
15+
"github.com/pkg/errors"
16+
"github.com/replicatedhq/ship/pkg/constants"
17+
)
18+
19+
func (r *resolver) resolveRunbookRelease() (*ShipRelease, error) {
20+
debug := level.Debug(log.With(r.Logger, "method", "resolveRunbookRelease"))
21+
debug.Log("phase", "load-specs", "from", "runbook", "file", r.Runbook)
22+
23+
specYAML, err := r.FS.ReadFile(r.Runbook)
24+
if err != nil {
25+
return nil, errors.Wrapf(err, "read specs from %s", r.Runbook)
26+
}
27+
debug.Log("phase", "load-specs", "from", "runbook", "file", r.Runbook, "spec", specYAML)
28+
29+
if err := r.persistSpec(specYAML); err != nil {
30+
return nil, errors.Wrapf(err, "serialize last-used YAML to disk")
31+
}
32+
debug.Log("phase", "write-yaml", "from", r.Runbook, "write-location", constants.ReleasePath)
33+
34+
fakeGithubContents, err := r.loadLocalGitHubContents()
35+
if err != nil {
36+
return nil, errors.Wrapf(err, "load fake github contents")
37+
}
38+
39+
return &ShipRelease{
40+
Spec: string(specYAML),
41+
ChannelName: r.SetChannelName,
42+
ChannelIcon: r.SetChannelIcon,
43+
Semver: r.RunbookReleaseSemver,
44+
GithubContents: fakeGithubContents,
45+
}, nil
46+
}
47+
48+
func (r *resolver) loadLocalGitHubContents() ([]GithubContent, error) {
49+
debug := level.Debug(log.With(r.Logger, "method", "loadLocalGitHubContents"))
50+
var fakeGithubContents []GithubContent
51+
for _, content := range r.SetGitHubContents {
52+
debug.Log("event", "githubcontents.set", "received", content)
53+
split := strings.Split(content, ":")
54+
if len(split) != 4 {
55+
return nil, errors.Errorf("set-github-contents %q invalid, expected a REPO:REPO_PATH:REF:LOCAL_PATH", content)
56+
}
57+
repo := split[0]
58+
repoPath := split[1]
59+
ref := split[2]
60+
localpath := split[3]
61+
62+
debug.Log("event", "githubcontents.loadFiles", "localPath", localpath)
63+
files, err := r.loadLocalGithubFiles(localpath, repoPath)
64+
if err != nil {
65+
return nil, errors.Wrapf(err, "set github files")
66+
}
67+
68+
fakeGithubContents = append(fakeGithubContents, GithubContent{
69+
Repo: repo,
70+
Path: repoPath,
71+
Ref: ref,
72+
Files: files,
73+
})
74+
debug.Log("event", "githubcontents.set.finished", "received", content)
75+
}
76+
return fakeGithubContents, nil
77+
}
78+
79+
func (r *resolver) loadLocalGithubFiles(localpath string, repoPath string) ([]GithubFile, error) {
80+
debug := level.Debug(log.With(r.Logger, "method", "loadLocalGitHubFiles"))
81+
var files []GithubFile
82+
err := r.FS.Walk(localpath, func(path string, info os.FileInfo, err error) error {
83+
if err != nil {
84+
return errors.Wrapf(err, "walk %s from %s", info.Name(), path)
85+
}
86+
87+
if info.IsDir() {
88+
return nil
89+
}
90+
91+
walkRepoPath := strings.TrimPrefix(path, localpath)
92+
if !strings.HasPrefix(walkRepoPath, repoPath) {
93+
return nil
94+
}
95+
96+
contents, err := r.FS.ReadFile(path)
97+
if err != nil {
98+
return errors.Wrapf(err, "read %s from %s", info.Name(), path)
99+
}
100+
debug.Log("event", "githubcontents.loadFile.complete", "path", path, "name", info.Name())
101+
102+
encodedData := &bytes.Buffer{}
103+
encoder := base64.NewEncoder(base64.StdEncoding, encodedData)
104+
defer encoder.Close()
105+
encoder.Write(contents)
106+
sha := fmt.Sprintf("%x", sha256.Sum256(contents))
107+
files = append(files, GithubFile{
108+
Name: info.Name(),
109+
Path: walkRepoPath,
110+
Sha: sha,
111+
Size: info.Size(),
112+
Data: encodedData.String(),
113+
})
114+
return nil
115+
})
116+
return files, err
117+
}

pkg/specs/replicatedapp/local_test.go

+126
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package replicatedapp
2+
3+
import (
4+
"testing"
5+
6+
"github.com/replicatedhq/ship/pkg/testing/logger"
7+
"github.com/spf13/afero"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestLoadLocalGitHubContents(t *testing.T) {
12+
tests := []struct {
13+
name string
14+
githubContent []string
15+
fs map[string]string
16+
expectContents []GithubContent
17+
}{
18+
{
19+
name: "none to set",
20+
githubContent: nil,
21+
expectContents: nil,
22+
},
23+
{
24+
name: "set one file",
25+
fs: map[string]string{
26+
"/foo/bar.txt": "some-contents",
27+
},
28+
githubContent: []string{"replicatedhq/test-stuff:/bar.txt:master:/foo"},
29+
expectContents: []GithubContent{
30+
{
31+
Repo: "replicatedhq/test-stuff",
32+
Path: "/bar.txt",
33+
Ref: "master",
34+
Files: []GithubFile{
35+
{
36+
Path: "/bar.txt",
37+
Name: "bar.txt",
38+
Size: 13,
39+
Sha: "6e32ea34db1b3755d7dec972eb72c705338f0dd8e0be881d966963438fb2e800",
40+
Data: "c29tZS1jb250ZW50",
41+
},
42+
},
43+
},
44+
},
45+
},
46+
{
47+
name: "set many files from two repos",
48+
fs: map[string]string{
49+
"/foo/bar.txt": "some-contents",
50+
"/foo/baz.txt": "some-contents",
51+
"/foo/bar/baz.txt": "some-contents",
52+
"/spam/eggs.txt": "some-other-contents",
53+
},
54+
githubContent: []string{
55+
"replicatedhq/test-stuff:/:master:/foo",
56+
"replicatedhq/other-tests:/eggs.txt:release:/spam",
57+
},
58+
expectContents: []GithubContent{
59+
{
60+
Repo: "replicatedhq/test-stuff",
61+
Path: "/",
62+
Ref: "master",
63+
Files: []GithubFile{
64+
{
65+
Path: "/bar/baz.txt",
66+
Name: "baz.txt",
67+
Size: 13,
68+
Sha: "6e32ea34db1b3755d7dec972eb72c705338f0dd8e0be881d966963438fb2e800",
69+
Data: "c29tZS1jb250ZW50",
70+
},
71+
{
72+
Path: "/bar.txt",
73+
Name: "bar.txt",
74+
Size: 13,
75+
Sha: "6e32ea34db1b3755d7dec972eb72c705338f0dd8e0be881d966963438fb2e800",
76+
Data: "c29tZS1jb250ZW50",
77+
},
78+
{
79+
Path: "/baz.txt",
80+
Name: "baz.txt",
81+
Size: 13,
82+
Sha: "6e32ea34db1b3755d7dec972eb72c705338f0dd8e0be881d966963438fb2e800",
83+
Data: "c29tZS1jb250ZW50",
84+
},
85+
},
86+
},
87+
{
88+
Repo: "replicatedhq/other-tests",
89+
Path: "/eggs.txt",
90+
Ref: "release",
91+
Files: []GithubFile{
92+
{
93+
Path: "/eggs.txt",
94+
Name: "eggs.txt",
95+
Size: 19,
96+
Sha: "a2c0a8c54d71e14e9533749c32716c12f92f61294dfdce4f3b4c07303c0119b0",
97+
Data: "c29tZS1vdGhlci1jb250ZW50",
98+
},
99+
},
100+
},
101+
},
102+
},
103+
}
104+
for _, test := range tests {
105+
t.Run(test.name, func(t *testing.T) {
106+
req := require.New(t)
107+
mockFs := afero.Afero{Fs: afero.NewMemMapFs()}
108+
109+
for key, value := range test.fs {
110+
err := mockFs.WriteFile(key, []byte(value), 0777)
111+
req.NoError(err)
112+
}
113+
114+
resolver := &resolver{
115+
Logger: &logger.TestLogger{T: t},
116+
FS: mockFs,
117+
SetGitHubContents: test.githubContent,
118+
}
119+
120+
result, err := resolver.loadLocalGitHubContents()
121+
122+
req.NoError(err)
123+
req.Equal(test.expectContents, result)
124+
})
125+
}
126+
}

0 commit comments

Comments
 (0)