Skip to content

Commit 9e03068

Browse files
committed
fix: trigger parser on deployment updates (#8834)
* feat: trigger parser on deployment updates: * pull is not required
1 parent be9cfd2 commit 9e03068

File tree

2 files changed

+136
-1
lines changed

2 files changed

+136
-1
lines changed

cli/cmd/project/deployment/deployments_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66
"testing"
77
"time"
88

9+
"github.com/google/go-github/v71/github"
910
"github.com/rilldata/rill/admin/testadmin"
11+
"github.com/rilldata/rill/cli/pkg/gitutil"
1012
"github.com/rilldata/rill/cli/testcli"
1113
adminv1 "github.com/rilldata/rill/proto/gen/rill/admin/v1"
1214
"github.com/rilldata/rill/runtime/drivers"
@@ -118,6 +120,127 @@ vars:
118120
}, 10*time.Second, 100*time.Millisecond, "unexpected model output after env set post restart")
119121
}
120122

123+
func TestPrimaryBranchChange(t *testing.T) {
124+
testmode.Expensive(t)
125+
126+
adm := testadmin.NewWithOptionalRuntime(t, true)
127+
_, c := adm.NewUser(t)
128+
u1 := testcli.New(t, adm, c.Token)
129+
130+
result := u1.Run(t, "org", "create", "branch-change-test")
131+
require.Equal(t, 0, result.ExitCode)
132+
133+
// deploy the project on main branch
134+
tempDir := t.TempDir()
135+
putFiles(t, tempDir, map[string]string{"rill.yaml": `compiler: rillv1
136+
display_name: Branch Change Test
137+
olap_connector: duckdb`,
138+
})
139+
putFiles(t, tempDir, map[string]string{"models/model.sql": "SELECT 'main' AS branch"})
140+
result = u1.Run(t, "project", "deploy", "--interactive=false", "--org=branch-change-test", "--project=branch-test", "--path="+tempDir)
141+
require.Equal(t, 0, result.ExitCode, result.Output)
142+
143+
// manually trigger deployment
144+
depl := adm.TriggerDeployment(t, "branch-change-test", "branch-test")
145+
146+
// check model output from main branch
147+
checkModelOutput := func() (string, error) {
148+
olap, release, err := adm.Runtime.OLAP(t.Context(), depl.RuntimeInstanceID, "duckdb")
149+
if err != nil {
150+
return "", err
151+
}
152+
defer release()
153+
154+
rows, err := olap.Query(t.Context(), &drivers.Statement{Query: "SELECT branch FROM model"})
155+
if err != nil {
156+
return "", err
157+
}
158+
defer rows.Close()
159+
160+
var res string
161+
for rows.Next() {
162+
if err := rows.Scan(&res); err != nil {
163+
return "", err
164+
}
165+
}
166+
if err := rows.Err(); err != nil {
167+
return "", err
168+
}
169+
return res, nil
170+
}
171+
require.Eventually(t, func() bool {
172+
branch, _ := checkModelOutput()
173+
return branch == "main"
174+
}, 10*time.Second, 100*time.Millisecond, "expected model output to be 'main'")
175+
176+
// get project to find git remote
177+
proj, err := c.GetProject(t.Context(), &adminv1.GetProjectRequest{
178+
Org: "branch-change-test",
179+
Project: "branch-test",
180+
})
181+
require.NoError(t, err)
182+
183+
// create a new branch in github with updated model
184+
installationID, err := adm.Admin.Github.ManagedOrgInstallationID()
185+
require.NoError(t, err)
186+
ghClient := adm.Admin.Github.InstallationClient(installationID, nil)
187+
188+
owner, repo, ok := gitutil.SplitGithubRemote(proj.Project.GitRemote)
189+
require.True(t, ok, "invalid github remote: %s", proj.Project.GitRemote)
190+
191+
// get the current main branch ref
192+
mainRef, _, err := ghClient.Git.GetRef(t.Context(), owner, repo, "refs/heads/main")
193+
require.NoError(t, err)
194+
195+
// create new branch "feature" from main
196+
newBranchRef := "refs/heads/feature"
197+
_, _, err = ghClient.Git.CreateRef(t.Context(), owner, repo, &github.Reference{
198+
Ref: &newBranchRef,
199+
Object: &github.GitObject{SHA: mainRef.Object.SHA},
200+
})
201+
require.NoError(t, err)
202+
203+
// update model.sql in the feature branch
204+
fileContent := "SELECT 'feature' AS branch"
205+
filePath := "models/model.sql"
206+
207+
// get current file to get its SHA
208+
fileInfo, _, _, err := ghClient.Repositories.GetContents(t.Context(), owner, repo, filePath, &github.RepositoryContentGetOptions{
209+
Ref: "feature",
210+
})
211+
require.NoError(t, err)
212+
213+
// update file in feature branch
214+
_, _, err = ghClient.Repositories.UpdateFile(t.Context(), owner, repo, filePath, &github.RepositoryContentFileOptions{
215+
Message: github.Ptr("Update model for feature branch"),
216+
Content: []byte(fileContent),
217+
SHA: fileInfo.SHA,
218+
Branch: github.Ptr("feature"),
219+
})
220+
require.NoError(t, err)
221+
222+
// change primary branch using project edit
223+
result = u1.Run(t, "project", "edit", "--primary-branch=feature", "--project=branch-test", "--org=branch-change-test")
224+
require.Equal(t, 0, result.ExitCode, result.Output)
225+
226+
// verify project primary branch is updated
227+
proj, err = c.GetProject(t.Context(), &adminv1.GetProjectRequest{
228+
Org: "branch-change-test",
229+
Project: "branch-test",
230+
})
231+
require.NoError(t, err)
232+
require.Equal(t, "feature", proj.Project.PrimaryBranch)
233+
234+
// manually trigger deployment to pick up new branch
235+
depl = adm.TriggerDeployment(t, "branch-change-test", "branch-test")
236+
237+
// verify model is updated with changes from feature branch
238+
require.Eventually(t, func() bool {
239+
branch, _ := checkModelOutput()
240+
return branch == "feature"
241+
}, 10*time.Second, 100*time.Millisecond, "expected model output to be 'feature' after branch change")
242+
}
243+
121244
func putFiles(t *testing.T, baseDir string, files map[string]string) {
122245
for path, content := range files {
123246
path = filepath.Join(baseDir, path)

runtime/config_reloader.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package runtime
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"maps"
78
"time"
89

@@ -125,7 +126,7 @@ func (r *configReloader) reloadConfig(ctx context.Context, instanceID string) er
125126
}
126127
defer release()
127128

128-
err = repo.Pull(ctx, &drivers.PullOptions{ForceHandshake: true, UserTriggered: true})
129+
err = repo.Pull(ctx, &drivers.PullOptions{ForceHandshake: true})
129130
if err != nil {
130131
r.rt.Logger.Error("ReloadConfig: failed to pull repo", zap.String("instance_id", inst.ID), zap.Error(err), observability.ZapCtx(ctx))
131132
}
@@ -134,6 +135,17 @@ func (r *configReloader) reloadConfig(ctx context.Context, instanceID string) er
134135
r.updatedOn[instanceID] = cfg.UpdatedOn
135136
// changes in archive asset IDs are correctly propogated via repo connection reopen only
136137
restartController = restartController || cfg.UsesArchive
138+
if !restartController {
139+
// retrigger parser to pick up changes
140+
ctrl, err := r.rt.Controller(ctx, instanceID)
141+
if err != nil {
142+
return err
143+
}
144+
err = ctrl.Reconcile(ctx, GlobalProjectParserName)
145+
if err != nil {
146+
return fmt.Errorf("failed to trigger parser: %w", err)
147+
}
148+
}
137149
}
138150

139151
err = r.rt.EditInstance(ctx, inst, restartController)

0 commit comments

Comments
 (0)