profile
viewpoint
Thorsten Ball mrnugget @sourcegraph Bavaria, Germany https://thorstenball.com Author of interpreterbook.com and compilerbook.com. I like to program where the rubber hits the road — wherever that may be.

felixge/node-ar-drone 1686

A node.js client for controlling Parrot AR Drone 2.0 quad-copters.

mrnugget/dotfiles 18

My dotfiles, including configuration for zsh, tmux, psql

mrnugget/awesome-compilers 14

:sunglasses: Curated list of awesome resources on Compilers, Interpreters and Runtimes.

mrnugget/data_structures 6

Implementations of common data structures in C

mrnugget/anygood 5

Was the movie any good? Check the ratings from different sites!

mrnugget/craftinginterpreters 2

Repository for the book "Crafting Interpreters"

mrnugget/emacs.d 1

Emacs configuration of a Vim lover

mrnugget/font-awesome-rails 1

the font-awesome font bundled as an asset for the rails asset pipeline

ans82/church-curator-development 0

Church Curators Public Issue Tracker

mrnugget/activerecord-postgis-adapter 0

ActiveRecord connection adapter for PostGIS, based on postgresql and rgeo

PR opened sourcegraph/sourcegraph

Reviewers
Add CampaignSpec.DiffStat property

This fixes #12564 by adding the DiffStat property to the CampaignSpec.

+69 -5

0 comment

7 changed files

pr created time in 5 hours

create barnchsourcegraph/sourcegraph

branch : mrn/campaign-spec-diffstat

created branch time in 5 hours

issue closedsourcegraph/sourcegraph

Migrate existing campaigns' names to the new slug-like format

If we decide against https://github.com/sourcegraph/sourcegraph/issues/12641 and https://github.com/sourcegraph/sourcegraph/issues/12640 we need a migration that migrates existing campaigns' names into the new sluggable format, where no whitespaces are allowed (see campaignSpec.schema.json)

closed time in 5 hours

mrnugget

issue commentsourcegraph/sourcegraph

Migrate existing campaigns' names to the new slug-like format

Closing this because we decided for https://github.com/sourcegraph/sourcegraph/issues/12640, meaning that we will offer an export option and then delete all existing campaigns.

mrnugget

comment created time in 5 hours

push eventsourcegraph/sourcegraph

Erik Seliger

commit sha a2622616876c7d95d5fa6da19e3ccbf75d5f2189

Fix scanner when processAfter is NULL (#12837) * Fix scanner when processAfter is NULL * Handle nullable timestamps on changesets table correctly Co-authored-by: Thorsten Ball <mrnugget@gmail.com>

view details

push time in 5 hours

delete branch sourcegraph/sourcegraph

delete branch : es/fix-scan-process-after

delete time in 5 hours

PR merged sourcegraph/sourcegraph

Fix scanner when processAfter is NULL

Had NULL in my database and the campaigns list page didn't work with an error, this fixes it.

+14 -13

1 comment

2 changed files

eseliger

pr closed time in 5 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha 046db3b75be1419c51b00d612559e294f6fa8d68

Handle nullable timestamps on changesets table correctly

view details

push time in 6 hours

pull request commentsourcegraph/sourcegraph

Fix scanner when processAfter is NULL

Ah. StartedAt, FinishedAt, ProcessAfter all need this. Let me push something to this branch.

eseliger

comment created time in 6 hours

pull request commentsourcegraph/sourcegraph

Enable Campaigns by default (with write-access only for admins)

@sourcegraph/campaigns @sqs I rebased this PR onto main so that it's ready to be merged, once we're comfortable merging it. I also updated the PR description, but here's what it does:

  • It replaces all the feature flags with a single campaigns.enabled flag that enables/disables campaigns for all users. It's on by default.
  • I left in the safety switch: creating changesetSpecs/campaignSpecs (which are the entry points to all other write-operations in campaigns) is only allowed for admins.

That means: by merging this campaigns are enabled by default but only admins can create campaigns. I think that's fine.

@eseliger due to the large rebase across 1.5 months of commits I lost the changes in the frontend. Could you take a quick look?

mrnugget

comment created time in 6 hours

push eventsourcegraph/sourcegraph

Keegan Carruthers-Smith

commit sha a1e179344d5962f9442018eeba24cb532bbf1570

search: always enable searchMultipleRevisionsPerRepository (#11671) This feature is ready for use. It is also required for Version Contexts to function with a repository being specified more than once. The CHANGELOG entry is copied from the original addition of the experimental feature.

view details

Keegan Carruthers-Smith

commit sha c073a0503df6db9b493d99a2add608f4e028586d

grafana: avoid copying go files in tree (#11673) The grafana build script temporarily copies ./monitoring to ./docker-images/grafana/monitoring. ./monitoring contains go files. However, whenever go files change our dev script would want to rebuild the things that have changed. So we would get into this state where the grafana build script would keep adding and removing go files, and our dev script would keep deciding we need to rebuild grafana. This commit does the build out of tree instead, to avoid the watch script seeing the go files come and go. An alternative implementation would be adding an ignore to the temporary directory, but that requires touching more places. Instead we adjust the hack used by grafana/build.sh.

view details

Keegan Carruthers-Smith

commit sha 0653305db9f2f0c0d8bd59fd9e2bb94d3b313426

zoekt: treat repo names case sensitively (#11670) Previously we treated zoekt repositories as if the case would not match what is returned from the repo database. However, Zoekt requires the correct caseing of the repo name to do the search. Additionally Zoekt uses the correct casing of the repo name when indexing. So the uses of ToLower when checking if a repo was indexed was unnecessary. Additionally in the case a repository name changed case we would incorrectly believe we have indexed the repo, but the actual zoekt search would fail due to the index being case sensitive. Additionally avoiding the calls to ToLower should improve performance. However, I didn't validate this (but naively it should).

view details

Keegan Carruthers-Smith

commit sha 9d90dc58264abf3d59159024f229efcf0ce6fd23

dev: watchman ignores hidden files (#11674) I noticed editors and other tools will often write to temporarily hidden files. This excludes them from triggering a rebuild. Note: we will still watch hidden directories, just not hidden files.

view details

Stephen Gutekanst

commit sha 53afb2e12e35b24d4138eee31767241f705fe44d

Update latest release to 3.17.1 (#11681)

view details

Stephen Gutekanst

commit sha c301ecd873b9ee96afc1022319c86eaaf202bc1b

CHANGELOG: release 3.17.1 + add missing changelog entries (#11682)

view details

Renovate Bot

commit sha c8f8f2e8a6ae5758c7e52e122cde9c47f9b817c8

Update dependency eslint to ^7.3.1

view details

Rijnard van Tonder

commit sha ed7ea1103f4d96fc1cdd45d92b50106f160725f7

docs: add search query language reference (#11444)

view details

Rob Rhyne

commit sha 9a4fcf4514bc395b65e8a764bc85eff92c1be758

Code host config message (#11645) * Update code host config message The message "Configure external services" did not clearly indicate the intension to add code hosts. This commit improves that message. * Fix test for previous commit

view details

Thorsten Ball

commit sha 7926b6c5b035efd0b836dafab1b5f4c1894428bd

Replace redacted Docker image in prometheus/Dockerfile (#11687) Builds on `master` are failing because they can't build the Docker images. This should fix it.

view details

renovate[bot]

commit sha 2c2dcb66ea12ab2dd874a9f15685729be4fc3f7e

Update dependency @sourcegraph/eslint-config to ^0.19.3 (#11683) Co-authored-by: Renovate Bot <bot@renovateapp.com> Co-authored-by: Felix Becker <felix.b@outlook.com>

view details

Loïc Guychard

commit sha cd10c1cc2f4b98e2e5641d2987f51229d52205a5

Add support for parenthesized parameters in frontend query parser (#11688) Fixes #11655 Adds naive parsing of opening & closing parentheses (no grouping / paren matching), to avoid showing squiggles on queries like `r:a (f:b and c)`, where we previously interpreted `(f:b` as a single literal and suggested quoting it because it contained a colon.

view details

Renovate Bot

commit sha 06aed5b1b171981b6befab62bfd1f653fc6166a0

Update dependency @sourcegraph/eslint-config to ^0.19.4

view details

Keegan Carruthers-Smith

commit sha 99b33349b12b931dbcbcf67d28c2a2fda602a080

repo-updater: test Syncer.Run and Synced chan (#11691) This test ensures our main entry point to Syncer is tested. Additionally it tests some properties we expect our Synced channel to exhibit. This is useful behaviour to assert since our git scheduler relies on it.

view details

Keegan Carruthers-Smith

commit sha 9356045d731984d93a140387bcb6cdbd0afa897d

repo-updater: remove streaming syncer feature flag (#11690) It has defaulted to on for many releases. Additionally I noticed a bug. If SyncedSubset is nil we should not do streaming inserts. The reason is Synced will never have the Diff.Added field populated since it will always be via subset synced. So this would lead to the repo not being noticed in the git update scheduler. However, we always have SubsetSynced set (except in tests). So this was a benign bug.

view details

Keegan Carruthers-Smith

commit sha eaf283177dabb8e9994df5f3ae14bb53fa817119

repo-updater: populate git fetch scheduler on startup (#11692)

view details

Eric Fritz

commit sha 55123e2de95cdbf8dd1709dac321ecec82be10df

codeintel: Catch repo not found error in repository resolvers (#11657)

view details

Eric Fritz

commit sha de1bb279576fde7f8dd4f9658f6b18230e0d1b5a

codeintel: Find closest dumps that intersect path (#11644)

view details

Adam Harvey

commit sha f1fac0001ec84b0fab1a476c0655f835677979ee

campaigns: Cache external changeset diffstats (#11279)

view details

Gonzalo Peci

commit sha 2f688d63fb5694b8b0b5967b5ecb4534adc4a795

Add instructions for Caddy certificate on WSL (#11679) Caddy certificate does not get automatically added to the Windows certificate store and we need to add it manually

view details

push time in 6 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha 3570dc43699275cddeffb82e87e88d51179b4e04

Clean up campaigns/resolvers tests (#12829) * Query campaign's changesets in TestApplyCampaign * Remove unused test helper function * Move all resolver test helpers into same file * Remove more test helpers

view details

push time in 8 hours

delete branch sourcegraph/sourcegraph

delete branch : mrn/resolver-tests-fixes

delete time in 8 hours

PR merged sourcegraph/sourcegraph

Clean up campaigns/resolvers tests

This does a few small things:

  • Remove a TODO in TestApplyCampaign and replace it with actual assertion
  • Remove an unused test helper
  • Move TestChangeseCountsOverTime to the campaigns_test.go file where the resolver is on which the method is defined
  • Move all test helpers into main_test.go instead of testing.go
+377 -447

1 comment

3 changed files

mrnugget

pr closed time in 8 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

 func (r *campaignResolver) ChangesetCountsOverTime(  	resolvers := []graphqlbackend.ChangesetCountsResolver{} -	opts := ee.ListChangesetsOpts{CampaignID: r.Campaign.ID, Limit: -1}+	publishedState := campaigns.ChangesetPublicationStatePublished+	opts := ee.ListChangesetsOpts{CampaignID: r.Campaign.ID, Limit: -1, PublicationState: &publishedState} 	cs, _, err := r.store.ListChangesets(ctx, opts) 	if err != nil { 		return resolvers, err 	} -	weekAgo := time.Now().Add(-7 * 24 * time.Hour)+	now := r.store.Clock()()++	weekAgo := now.Add(-6 * 24 * time.Hour)

Hmmm. I honestly don't think it matters. Ideally a user could set the start/end times in the UI in the future.

eseliger

comment created time in 8 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

 import ( 	"github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/resolvers/apitest" 	ct "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/testing" 	"github.com/sourcegraph/sourcegraph/internal/actor"-	"github.com/sourcegraph/sourcegraph/internal/api" 	"github.com/sourcegraph/sourcegraph/internal/campaigns" 	"github.com/sourcegraph/sourcegraph/internal/db" 	"github.com/sourcegraph/sourcegraph/internal/db/dbconn" 	"github.com/sourcegraph/sourcegraph/internal/db/dbtesting"-	"github.com/sourcegraph/sourcegraph/internal/extsvc"-	"github.com/sourcegraph/sourcegraph/internal/httptestutil"-	"github.com/sourcegraph/sourcegraph/internal/rcache"-	"github.com/sourcegraph/sourcegraph/internal/repoupdater/protocol"-	"github.com/sourcegraph/sourcegraph/schema" ) -func TestChangesetCountsOverTime(t *testing.T) {

Thanks!

eseliger

comment created time in 8 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

+package resolvers++import (+	"context"+	"database/sql"+	"os"+	"reflect"+	"testing"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/backend"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"+	"github.com/sourcegraph/sourcegraph/cmd/repo-updater/repos"+	ee "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/resolvers/apitest"+	ct "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/testing"+	"github.com/sourcegraph/sourcegraph/internal/actor"+	"github.com/sourcegraph/sourcegraph/internal/api"+	"github.com/sourcegraph/sourcegraph/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/db/dbconn"+	"github.com/sourcegraph/sourcegraph/internal/db/dbtesting"+	"github.com/sourcegraph/sourcegraph/internal/extsvc"+	"github.com/sourcegraph/sourcegraph/internal/extsvc/github"+	"github.com/sourcegraph/sourcegraph/internal/httptestutil"+	"github.com/sourcegraph/sourcegraph/internal/rcache"+	"github.com/sourcegraph/sourcegraph/internal/repoupdater/protocol"+	"github.com/sourcegraph/sourcegraph/schema"+)++func TestChangesetCountsOverTimeResolver(t *testing.T) {+	if testing.Short() {+		t.Skip()+	}++	ctx := backend.WithAuthzBypass(context.Background())+	dbtesting.SetupGlobalTestDB(t)++	userID := insertTestUser(t, dbconn.Global, "changeset-count-resolver", true)++	now := time.Now().UTC().Truncate(time.Microsecond)+	clock := func() time.Time {+		return now.UTC().Truncate(time.Microsecond)+	}+	store := ee.NewStoreWithClock(dbconn.Global, clock)+	rstore := repos.NewDBStore(dbconn.Global, sql.TxOptions{})++	repo := newGitHubTestRepo("github.com/sourcegraph/sourcegraph", 1)+	if err := rstore.UpsertRepos(ctx, repo); err != nil {+		t.Fatal(err)+	}++	campaign := &campaigns.Campaign{+		Name:            "my-unique-name",+		NamespaceUserID: userID,+		AuthorID:        userID,+	}+	if err := store.CreateCampaign(ctx, campaign); err != nil {+		t.Fatal(err)+	}++	changeset1 := createChangeset(t, ctx, store, testChangesetOpts{+		repo: repo.ID,+		// We don't need a spec because we don't query for fields that would+		// require it+		currentSpec:         0,+		externalServiceType: "github",+		// Unpublished changesets should not be considered.+		publicationState: campaigns.ChangesetPublicationStateUnpublished,+		ownedByCampaign:  campaign.ID,+		campaign:         campaign.ID,+	})+	changeset2 := createChangeset(t, ctx, store, testChangesetOpts{+		repo: repo.ID,+		// We don't need a spec because we don't query for fields that would+		// require it+		currentSpec:         0,+		externalServiceType: "github",+		externalState:       campaigns.ChangesetExternalStateOpen,+		publicationState:    campaigns.ChangesetPublicationStatePublished,+		ownedByCampaign:     campaign.ID,+		campaign:            campaign.ID,+		metadata: &github.PullRequest{+			CreatedAt: now.Add(-1 * 24 * time.Hour),+			TimelineItems: []github.TimelineItem{+				{Type: "MergedEvent", Item: &github.LabelEvent{+					CreatedAt: now,+				}},+			},+		},+	})++	addChangeset(t, ctx, store, campaign, changeset1.ID)+	addChangeset(t, ctx, store, campaign, changeset2.ID)++	s, err := graphqlbackend.NewSchema(&Resolver{store: store}, nil, nil)+	if err != nil {+		t.Fatal(err)+	}++	campaignAPIID := string(campaigns.MarshalCampaignID(campaign.ID))

Feel free to just ignore me here and think "this guy", but I have to: APIID is aesthetically ... well, it's not nice 😉 In the old tradition of JavaScript's XMLHttpRequest I'd say: overboard with all the casing and acronym rules and just use: ApiID.

(Yes, the time it took me to write this comment is completely out of proportion compared to the issue at hand...)

eseliger

comment created time in 8 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

+package resolvers++import (+	"context"+	"database/sql"+	"fmt"+	"testing"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/backend"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"+	"github.com/sourcegraph/sourcegraph/cmd/repo-updater/repos"+	ee "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/resolvers/apitest"+	"github.com/sourcegraph/sourcegraph/internal/actor"+	"github.com/sourcegraph/sourcegraph/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/db/dbconn"+	"github.com/sourcegraph/sourcegraph/internal/db/dbtesting"+	"github.com/sourcegraph/sourcegraph/internal/extsvc/github"+)++func TestChangesetEventConnectionResolver(t *testing.T) {+	if testing.Short() {+		t.Skip()+	}++	ctx := backend.WithAuthzBypass(context.Background())+	dbtesting.SetupGlobalTestDB(t)++	userID := insertTestUser(t, dbconn.Global, "changeset-event-connection-resolver", true)++	now := time.Now().UTC().Truncate(time.Microsecond)+	clock := func() time.Time {+		return now.UTC().Truncate(time.Microsecond)+	}+	store := ee.NewStoreWithClock(dbconn.Global, clock)+	rstore := repos.NewDBStore(dbconn.Global, sql.TxOptions{})++	repo := newGitHubTestRepo("github.com/sourcegraph/sourcegraph", 1)+	if err := rstore.UpsertRepos(ctx, repo); err != nil {+		t.Fatal(err)+	}++	campaign := &campaigns.Campaign{+		Name:            "my-unique-name",+		NamespaceUserID: userID,+		AuthorID:        userID,+	}+	if err := store.CreateCampaign(ctx, campaign); err != nil {+		t.Fatal(err)+	}++	changeset := createChangeset(t, ctx, store, testChangesetOpts{+		repo: repo.ID,+		// We don't need a spec because we don't query for fields that would+		// require it+		currentSpec:         0,+		externalServiceType: "github",+		publicationState:    campaigns.ChangesetPublicationStateUnpublished,+		externalReviewState: campaigns.ChangesetReviewStatePending,+		ownedByCampaign:     campaign.ID,+		campaign:            campaign.ID,+		metadata: &github.PullRequest{+			TimelineItems: []github.TimelineItem{+				{Type: "PullRequestCommit", Item: &github.PullRequestCommit{+					Commit: github.Commit{+						OID: "d34db33f",+					},+				}},+				{Type: "LabeledEvent", Item: &github.LabelEvent{+					Label: github.Label{+						ID:    "label-event",+						Name:  "cool-label",+						Color: "blue",+					},+				}},+			},+		},+	})++	addChangeset(t, ctx, store, campaign, changeset.ID)++	s, err := graphqlbackend.NewSchema(&Resolver{store: store}, nil, nil)+	if err != nil {+		t.Fatal(err)+	}++	changesetAPIID := string(marshalChangesetID(changeset.ID))+	nodes := []apitest.ChangesetEvent{+		{+			ID:        string(marshalChangesetEventID(1)),+			Changeset: struct{ ID string }{ID: changesetAPIID},+			CreatedAt: marshalDateTime(t, now),+		},+		{+			ID:        string(marshalChangesetEventID(2)),+			Changeset: struct{ ID string }{ID: changesetAPIID},+			CreatedAt: marshalDateTime(t, now),+		},+	}++	tests := []struct {+		firstParam      int+		wantHasNextPage bool+		wantTotalCount  int+		wantNodes       []apitest.ChangesetEvent+	}{+		{firstParam: 1, wantHasNextPage: true, wantTotalCount: 2, wantNodes: nodes[:1]},+		{firstParam: 2, wantHasNextPage: false, wantTotalCount: 2, wantNodes: nodes},+		{firstParam: 3, wantHasNextPage: false, wantTotalCount: 2, wantNodes: nodes},+	}++	for _, tc := range tests {+		t.Run(fmt.Sprintf("First %d", tc.firstParam), func(t *testing.T) {

Yep, it's me, Mr. Nitpick, and I'm here to suggest some things you can freely drag'n'drop into /dev/null:

		t.Run(fmt.Sprintf("first=%d", tc.firstParam), func(t *testing.T) {
eseliger

comment created time in 8 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

+package resolvers++import (+	"context"+	"database/sql"+	"os"+	"reflect"+	"testing"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/backend"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"+	"github.com/sourcegraph/sourcegraph/cmd/repo-updater/repos"+	ee "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/resolvers/apitest"+	ct "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/testing"+	"github.com/sourcegraph/sourcegraph/internal/actor"+	"github.com/sourcegraph/sourcegraph/internal/api"+	"github.com/sourcegraph/sourcegraph/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/db/dbconn"+	"github.com/sourcegraph/sourcegraph/internal/db/dbtesting"+	"github.com/sourcegraph/sourcegraph/internal/extsvc"+	"github.com/sourcegraph/sourcegraph/internal/extsvc/github"+	"github.com/sourcegraph/sourcegraph/internal/httptestutil"+	"github.com/sourcegraph/sourcegraph/internal/rcache"+	"github.com/sourcegraph/sourcegraph/internal/repoupdater/protocol"+	"github.com/sourcegraph/sourcegraph/schema"+)++func TestChangesetCountsOverTimeResolver(t *testing.T) {+	if testing.Short() {+		t.Skip()+	}++	ctx := backend.WithAuthzBypass(context.Background())+	dbtesting.SetupGlobalTestDB(t)++	userID := insertTestUser(t, dbconn.Global, "changeset-count-resolver", true)++	now := time.Now().UTC().Truncate(time.Microsecond)+	clock := func() time.Time {+		return now.UTC().Truncate(time.Microsecond)+	}+	store := ee.NewStoreWithClock(dbconn.Global, clock)+	rstore := repos.NewDBStore(dbconn.Global, sql.TxOptions{})++	repo := newGitHubTestRepo("github.com/sourcegraph/sourcegraph", 1)+	if err := rstore.UpsertRepos(ctx, repo); err != nil {+		t.Fatal(err)+	}++	campaign := &campaigns.Campaign{+		Name:            "my-unique-name",+		NamespaceUserID: userID,+		AuthorID:        userID,+	}+	if err := store.CreateCampaign(ctx, campaign); err != nil {+		t.Fatal(err)+	}++	changeset1 := createChangeset(t, ctx, store, testChangesetOpts{+		repo: repo.ID,+		// We don't need a spec because we don't query for fields that would+		// require it+		currentSpec:         0,

This comment has been duplicated so many times now, I think it's fine if we remove it altogether. I overestimated how much its needed.

eseliger

comment created time in 8 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

+package resolvers++import (+	"context"+	"database/sql"+	"os"+	"reflect"+	"testing"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/backend"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"+	"github.com/sourcegraph/sourcegraph/cmd/repo-updater/repos"+	ee "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/resolvers/apitest"+	ct "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/testing"+	"github.com/sourcegraph/sourcegraph/internal/actor"+	"github.com/sourcegraph/sourcegraph/internal/api"+	"github.com/sourcegraph/sourcegraph/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/db/dbconn"+	"github.com/sourcegraph/sourcegraph/internal/db/dbtesting"+	"github.com/sourcegraph/sourcegraph/internal/extsvc"+	"github.com/sourcegraph/sourcegraph/internal/extsvc/github"+	"github.com/sourcegraph/sourcegraph/internal/httptestutil"+	"github.com/sourcegraph/sourcegraph/internal/rcache"+	"github.com/sourcegraph/sourcegraph/internal/repoupdater/protocol"+	"github.com/sourcegraph/sourcegraph/schema"+)++func TestChangesetCountsOverTimeResolver(t *testing.T) {+	if testing.Short() {+		t.Skip()+	}++	ctx := backend.WithAuthzBypass(context.Background())+	dbtesting.SetupGlobalTestDB(t)++	userID := insertTestUser(t, dbconn.Global, "changeset-count-resolver", true)++	now := time.Now().UTC().Truncate(time.Microsecond)+	clock := func() time.Time {+		return now.UTC().Truncate(time.Microsecond)+	}+	store := ee.NewStoreWithClock(dbconn.Global, clock)+	rstore := repos.NewDBStore(dbconn.Global, sql.TxOptions{})++	repo := newGitHubTestRepo("github.com/sourcegraph/sourcegraph", 1)+	if err := rstore.UpsertRepos(ctx, repo); err != nil {+		t.Fatal(err)+	}++	campaign := &campaigns.Campaign{+		Name:            "my-unique-name",+		NamespaceUserID: userID,+		AuthorID:        userID,+	}+	if err := store.CreateCampaign(ctx, campaign); err != nil {+		t.Fatal(err)+	}++	changeset1 := createChangeset(t, ctx, store, testChangesetOpts{+		repo: repo.ID,+		// We don't need a spec because we don't query for fields that would+		// require it+		currentSpec:         0,+		externalServiceType: "github",+		// Unpublished changesets should not be considered.+		publicationState: campaigns.ChangesetPublicationStateUnpublished,+		ownedByCampaign:  campaign.ID,+		campaign:         campaign.ID,+	})+	changeset2 := createChangeset(t, ctx, store, testChangesetOpts{+		repo: repo.ID,+		// We don't need a spec because we don't query for fields that would+		// require it+		currentSpec:         0,+		externalServiceType: "github",+		externalState:       campaigns.ChangesetExternalStateOpen,+		publicationState:    campaigns.ChangesetPublicationStatePublished,+		ownedByCampaign:     campaign.ID,+		campaign:            campaign.ID,+		metadata: &github.PullRequest{+			CreatedAt: now.Add(-1 * 24 * time.Hour),+			TimelineItems: []github.TimelineItem{+				{Type: "MergedEvent", Item: &github.LabelEvent{+					CreatedAt: now,+				}},+			},+		},+	})++	addChangeset(t, ctx, store, campaign, changeset1.ID)+	addChangeset(t, ctx, store, campaign, changeset2.ID)++	s, err := graphqlbackend.NewSchema(&Resolver{store: store}, nil, nil)+	if err != nil {+		t.Fatal(err)+	}++	campaignAPIID := string(campaigns.MarshalCampaignID(campaign.ID))+	input := map[string]interface{}{"campaign": campaignAPIID}+	var response struct{ Node apitest.Campaign }+	apitest.MustExec(actor.WithActor(context.Background(), actor.FromUser(userID)), t, s, input, &response, queryChangesetCountsConnection)++	wantCounts := []apitest.ChangesetCounts{+		{Date: marshalDateTime(t, now.Add(-6*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-5*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-4*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-3*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-2*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-1*24*time.Hour)), Total: 1, Open: 1, OpenPending: 1},+		{Date: marshalDateTime(t, now), Total: 1, Merged: 1},+	}++	if diff := cmp.Diff(wantCounts, response.Node.ChangesetCountsOverTime); diff != "" {+		t.Fatalf("wrong changesets response (-want +got):\n%s", diff)+	}+}++const queryChangesetCountsConnection = `+query($campaign: ID!) {+  node(id: $campaign) {+    ... on Campaign {+            changesetCountsOverTime {+				date+				total+				merged+				closed+				open+				openApproved+				openChangesRequested+				openPending+			}+          }+        }+      }

Somebody left some whitespace lying around here! 😱

eseliger

comment created time in 9 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

+package resolvers++import (+	"context"+	"database/sql"+	"os"+	"reflect"+	"testing"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/backend"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"+	"github.com/sourcegraph/sourcegraph/cmd/repo-updater/repos"+	ee "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/resolvers/apitest"+	ct "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/testing"+	"github.com/sourcegraph/sourcegraph/internal/actor"+	"github.com/sourcegraph/sourcegraph/internal/api"+	"github.com/sourcegraph/sourcegraph/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/db/dbconn"+	"github.com/sourcegraph/sourcegraph/internal/db/dbtesting"+	"github.com/sourcegraph/sourcegraph/internal/extsvc"+	"github.com/sourcegraph/sourcegraph/internal/extsvc/github"+	"github.com/sourcegraph/sourcegraph/internal/httptestutil"+	"github.com/sourcegraph/sourcegraph/internal/rcache"+	"github.com/sourcegraph/sourcegraph/internal/repoupdater/protocol"+	"github.com/sourcegraph/sourcegraph/schema"+)++func TestChangesetCountsOverTimeResolver(t *testing.T) {+	if testing.Short() {+		t.Skip()+	}++	ctx := backend.WithAuthzBypass(context.Background())+	dbtesting.SetupGlobalTestDB(t)++	userID := insertTestUser(t, dbconn.Global, "changeset-count-resolver", true)++	now := time.Now().UTC().Truncate(time.Microsecond)+	clock := func() time.Time {+		return now.UTC().Truncate(time.Microsecond)+	}+	store := ee.NewStoreWithClock(dbconn.Global, clock)+	rstore := repos.NewDBStore(dbconn.Global, sql.TxOptions{})++	repo := newGitHubTestRepo("github.com/sourcegraph/sourcegraph", 1)+	if err := rstore.UpsertRepos(ctx, repo); err != nil {+		t.Fatal(err)+	}++	campaign := &campaigns.Campaign{+		Name:            "my-unique-name",+		NamespaceUserID: userID,+		AuthorID:        userID,+	}+	if err := store.CreateCampaign(ctx, campaign); err != nil {+		t.Fatal(err)+	}++	changeset1 := createChangeset(t, ctx, store, testChangesetOpts{+		repo: repo.ID,+		// We don't need a spec because we don't query for fields that would+		// require it+		currentSpec:         0,+		externalServiceType: "github",+		// Unpublished changesets should not be considered.+		publicationState: campaigns.ChangesetPublicationStateUnpublished,+		ownedByCampaign:  campaign.ID,+		campaign:         campaign.ID,+	})+	changeset2 := createChangeset(t, ctx, store, testChangesetOpts{+		repo: repo.ID,+		// We don't need a spec because we don't query for fields that would+		// require it+		currentSpec:         0,+		externalServiceType: "github",+		externalState:       campaigns.ChangesetExternalStateOpen,+		publicationState:    campaigns.ChangesetPublicationStatePublished,+		ownedByCampaign:     campaign.ID,+		campaign:            campaign.ID,+		metadata: &github.PullRequest{+			CreatedAt: now.Add(-1 * 24 * time.Hour),+			TimelineItems: []github.TimelineItem{+				{Type: "MergedEvent", Item: &github.LabelEvent{+					CreatedAt: now,+				}},+			},+		},+	})++	addChangeset(t, ctx, store, campaign, changeset1.ID)+	addChangeset(t, ctx, store, campaign, changeset2.ID)++	s, err := graphqlbackend.NewSchema(&Resolver{store: store}, nil, nil)+	if err != nil {+		t.Fatal(err)+	}++	campaignAPIID := string(campaigns.MarshalCampaignID(campaign.ID))+	input := map[string]interface{}{"campaign": campaignAPIID}+	var response struct{ Node apitest.Campaign }+	apitest.MustExec(actor.WithActor(context.Background(), actor.FromUser(userID)), t, s, input, &response, queryChangesetCountsConnection)++	wantCounts := []apitest.ChangesetCounts{+		{Date: marshalDateTime(t, now.Add(-6*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-5*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-4*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-3*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-2*24*time.Hour))},+		{Date: marshalDateTime(t, now.Add(-1*24*time.Hour)), Total: 1, Open: 1, OpenPending: 1},+		{Date: marshalDateTime(t, now), Total: 1, Merged: 1},+	}++	if diff := cmp.Diff(wantCounts, response.Node.ChangesetCountsOverTime); diff != "" {+		t.Fatalf("wrong changesets response (-want +got):\n%s", diff)+	}+}++const queryChangesetCountsConnection = `+query($campaign: ID!) {+  node(id: $campaign) {+    ... on Campaign {+            changesetCountsOverTime {+				date+				total+				merged+				closed+				open+				openApproved+				openChangesRequested+				openPending+			}+          }+        }+      }+`++func TestChangesetCountsOverTimeIntegration(t *testing.T) {

The funny thing is that this one does not go through the GraphQL layer but the other one does :)

eseliger

comment created time in 9 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

+package resolvers

Nitpick: file is called changeset_count.go but... I guess it should be changeset_counts.go (plural)

eseliger

comment created time in 9 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

+package resolvers++import (+	"context"+	"sync"++	"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend/graphqlutil"+	ee "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/httpcli"+)++type changesetEventsConnectionResolver struct {+	store       *ee.Store+	httpFactory *httpcli.Factory+	changeset   graphqlbackend.ExternalChangesetResolver
	changesetResolver   *changesetResolver
eseliger

comment created time in 9 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

+package resolvers++import (+	"context"+	"database/sql"+	"fmt"+	"testing"+	"time"++	"github.com/google/go-cmp/cmp"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/backend"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"+	"github.com/sourcegraph/sourcegraph/cmd/repo-updater/repos"+	ee "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/resolvers/apitest"+	"github.com/sourcegraph/sourcegraph/internal/actor"+	"github.com/sourcegraph/sourcegraph/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/db/dbconn"+	"github.com/sourcegraph/sourcegraph/internal/db/dbtesting"+	"github.com/sourcegraph/sourcegraph/internal/extsvc/github"+)++func TestChangesetEventConnectionResolver(t *testing.T) {+	if testing.Short() {+		t.Skip()+	}++	ctx := backend.WithAuthzBypass(context.Background())+	dbtesting.SetupGlobalTestDB(t)++	userID := insertTestUser(t, dbconn.Global, "changeset-event-connection-resolver", true)++	now := time.Now().UTC().Truncate(time.Microsecond)+	clock := func() time.Time {+		return now.UTC().Truncate(time.Microsecond)+	}+	store := ee.NewStoreWithClock(dbconn.Global, clock)+	rstore := repos.NewDBStore(dbconn.Global, sql.TxOptions{})++	repo := newGitHubTestRepo("github.com/sourcegraph/sourcegraph", 1)+	if err := rstore.UpsertRepos(ctx, repo); err != nil {+		t.Fatal(err)+	}++	campaign := &campaigns.Campaign{+		Name:            "my-unique-name",+		NamespaceUserID: userID,+		AuthorID:        userID,+	}+	if err := store.CreateCampaign(ctx, campaign); err != nil {+		t.Fatal(err)+	}++	changeset := createChangeset(t, ctx, store, testChangesetOpts{+		repo: repo.ID,+		// We don't need a spec because we don't query for fields that would+		// require it+		currentSpec:         0,

Another copy here.

eseliger

comment created time in 9 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

+package resolvers++import (+	"github.com/graph-gophers/graphql-go"+	"github.com/graph-gophers/graphql-go/relay"+	"github.com/sourcegraph/sourcegraph/cmd/frontend/graphqlbackend"+	ee "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/campaigns"+	"github.com/sourcegraph/sourcegraph/internal/httpcli"+)++type changesetEventResolver struct {+	store       *ee.Store+	httpFactory *httpcli.Factory+	changeset   graphqlbackend.ExternalChangesetResolver
	changesetResolver   *changesetResolver

I think it's better to only use the graphqlbackend interfaces at the boundary of our package, but not internally.

The explicit name is also better, IMHO.

eseliger

comment created time in 9 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

 func (r *campaignResolver) ChangesetCountsOverTime(  	resolvers := []graphqlbackend.ChangesetCountsResolver{} -	opts := ee.ListChangesetsOpts{CampaignID: r.Campaign.ID, Limit: -1}+	publishedState := campaigns.ChangesetPublicationStatePublished+	opts := ee.ListChangesetsOpts{CampaignID: r.Campaign.ID, Limit: -1, PublicationState: &publishedState} 	cs, _, err := r.store.ListChangesets(ctx, opts) 	if err != nil { 		return resolvers, err 	} -	weekAgo := time.Now().Add(-7 * 24 * time.Hour)+	now := r.store.Clock()()++	weekAgo := now.Add(-6 * 24 * time.Hour)

Are you sure about this? CalcCounts walks backwards in 24-hour steps, starting at end and stopping at a timestamp >= start. See: https://sourcegraph.com/github.com/sourcegraph/sourcegraph/-/blob/enterprise/internal/campaigns/counts.go#L107:6

And if you run

	fmt.Println(time.Now().Add(-6 * 24 * time.Hour))
	fmt.Println(time.Now().Add(-7 * 24 * time.Hour))

you'll get

2020-08-01 14:23:47.197073 +0200 CEST m=-518399.999897918
2020-07-31 14:23:47.197279 +0200 CEST m=-604799.999691835

The second one is last Friday, exactly a week ago, which is what we want to use as the lower boundary of the counts.

eseliger

comment created time in 9 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha b8d716c557932c03ecd3fd706934278c1bb99ff4

Remove leftover debug print statement (#12833)

view details

push time in 9 hours

delete branch sourcegraph/sourcegraph

delete branch : mrn/eat-leftovers

delete time in 9 hours

PR merged sourcegraph/sourcegraph

Remove leftover debug print statement

Erik "Eagle Eye" @eseliger spotted this one.

+0 -2

0 comment

1 changed file

mrnugget

pr closed time in 9 hours

PR opened sourcegraph/sourcegraph

Remove leftover debug print statement

Erik "Eagle Eye" @eseliger spotted this one.

+0 -2

0 comment

1 changed file

pr created time in 9 hours

create barnchsourcegraph/sourcegraph

branch : mrn/eat-leftovers

created branch time in 9 hours

Pull request review commentsourcegraph/sourcegraph

Allow listing a user's and org's campaigns

 func (r *Resolver) Campaigns(ctx context.Context, args *graphqlbackend.ListCampa 			opts.OnlyForAuthor = actor.UID 		} 	}++	if args.Namespace != nil {+		switch relay.UnmarshalKind(*args.Namespace) {+		case "User":+			err = relay.UnmarshalSpec(*args.Namespace, &opts.NamespaceUserID)+		case "Org":+			err = relay.UnmarshalSpec(*args.Namespace, &opts.NamespaceOrgID)+		default:+			err = errors.Errorf("Invalid namespace %q", *args.Namespace)+		}+		if err != nil {+			return nil, err+		}+	}++	fmt.Printf("opts=%+v\n", opts)+

🤦

mrnugget

comment created time in 9 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha 92f2d84c059d9c24983e681c7d215b9f722493c0

Fix "changesets processing?" checks in CloseCampaign/DeleteCampaign methods (#12782) * Remove leftover code from DeleteCampaign method * Fix "changesets processing?" check in CloseCampaign

view details

Thorsten Ball

commit sha 9b196b3e6d351647d628f7e54aa5fe809fa543fa

Query campaign's changesets in TestApplyCampaign

view details

Thorsten Ball

commit sha cedc331f8c53f44cd8bb223713c820a82da91734

Remove unused test helper function

view details

Thorsten Ball

commit sha 9b989d29fca8d4722e6f11ab53aa23262ec143d8

Move all resolver test helpers into same file

view details

Thorsten Ball

commit sha 46ddc659ef05880d46aacc6c7b996f4c28c3b41c

Remove more test helpers

view details

push time in 9 hours

pull request commentsourcegraph/sourcegraph

Clean up campaigns/resolvers tests

@eseliger I reverted the move of the TestChangesetCountsOvertime and removed the other two helpers — thanks!

mrnugget

comment created time in 9 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha 11fc1d9cd3dd7362c2fdec291079b9435b8bb149

Revert "Move TestChangesetCountsOverTime test to campaign_test.go" This reverts commit c80cba306f1d173233065f01b62bcd66cc21eb8e.

view details

push time in 9 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha 41066a85b60120a45040c9117b9903f1c17af617

Remove more test helpers

view details

push time in 9 hours

Pull request review commentsourcegraph/sourcegraph

Split up changeset_events file and add tests

 import ( 	"github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/resolvers/apitest" 	ct "github.com/sourcegraph/sourcegraph/enterprise/internal/campaigns/testing" 	"github.com/sourcegraph/sourcegraph/internal/actor"-	"github.com/sourcegraph/sourcegraph/internal/api" 	"github.com/sourcegraph/sourcegraph/internal/campaigns" 	"github.com/sourcegraph/sourcegraph/internal/db" 	"github.com/sourcegraph/sourcegraph/internal/db/dbconn" 	"github.com/sourcegraph/sourcegraph/internal/db/dbtesting"-	"github.com/sourcegraph/sourcegraph/internal/extsvc"-	"github.com/sourcegraph/sourcegraph/internal/httptestutil"-	"github.com/sourcegraph/sourcegraph/internal/rcache"-	"github.com/sourcegraph/sourcegraph/internal/repoupdater/protocol"-	"github.com/sourcegraph/sourcegraph/schema" ) -func TestChangesetCountsOverTime(t *testing.T) {

Yeah, agree. I was thinking about that too while looking at the test. But the old one, as big as it was, gave me more confidence, because it used real data from real PRs 🤔 Ideally, I think, we'd have a single test that has fully synced changesets from different code hosts in a campaign and queries all of their fields.

Could we leave the test as it was and create a ticket to split it up?

eseliger

comment created time in 9 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha 1e2ed4525b30ac9ab98044b69b4a0ffe0263a686

Return error when applying a campaign spec to a closed campaign (#12830) * Return error when trying to apply spec to closed campaign * Add and use a Campaign.Close() method

view details

push time in 9 hours

delete branch sourcegraph/sourcegraph

delete branch : mrn/apply-closed-campaign

delete time in 9 hours

PR merged sourcegraph/sourcegraph

Return error when applying a campaign spec to a closed campaign

See https://github.com/sourcegraph/sourcegraph/pull/12782#issuecomment-669908171 for more context.

Short version: it doesn't make sense to allow applying a new spec to a closed campaign.

+34 -5

0 comment

4 changed files

mrnugget

pr closed time in 9 hours

PR opened sourcegraph/sourcegraph

Return error when applying a campaign spec to a closed campaign

See https://github.com/sourcegraph/sourcegraph/pull/12782#issuecomment-669908171 for more context.

Short version: it doesn't make sense to allow applying a new spec to a closed campaign.

+34 -5

0 comment

4 changed files

pr created time in 11 hours

create barnchsourcegraph/sourcegraph

branch : mrn/apply-closed-campaign

created branch time in 11 hours

PR opened sourcegraph/sourcegraph

Clean up campaigns/resolvers tests

This does a few small things:

  • [ ] Remove a TODO in TestApplyCampaign and replace it with actual assertion
  • [ ] Remove an unused test helper
  • [ ] Move TestChangeseCountsOverTime to the campaigns_test.go file where the resolver is on which the method is defined
  • [ ] Move all test helpers into main_test.go instead of testing.go
+567 -572

0 comment

4 changed files

pr created time in 11 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha c47b6f1734b42179d870a7f546a5f1b0454cdbb6

Move all resolver test helpers into same file

view details

push time in 11 hours

create barnchsourcegraph/sourcegraph

branch : mrn/resolver-tests-fixes

created branch time in 12 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha 92f2d84c059d9c24983e681c7d215b9f722493c0

Fix "changesets processing?" checks in CloseCampaign/DeleteCampaign methods (#12782) * Remove leftover code from DeleteCampaign method * Fix "changesets processing?" check in CloseCampaign

view details

push time in 12 hours

delete branch sourcegraph/sourcegraph

delete branch : mrn/fix-processing-checks

delete time in 12 hours

PR merged sourcegraph/sourcegraph

Fix "changesets processing?" checks in CloseCampaign/DeleteCampaign methods

This fixes #12643 by fixing the checks and updating the methods to the new workflow model.

  1. DeleteCampaign won't have a check anymore, since it also doesn't accept the closeChangesets: bool parameter anymore. That's documented in the GraphQL API and we should make that visible in the UI.
  2. CloseCampaign now only returns an error if the user wants to close the open changesets and the changesets are processing.

Why do we return an error? Because if the user wants to close the open changesets, we can't guarantee that we close all of them if they're currently processing.

In the future, when we move the closing of changesets to the reconciler, we can revisit this. But even then it'll be hard to mark a changeset as closed and enqueue it for the reconciler while the reconciler is working on it. Since the reconciler would overwrite the the reconciler_state field. But that can be worked around by, say, introducing a to_be_closed field and a cron-like job enqueuing these changesets again and again until the field is false and the external_state is closed.

+129 -57

3 comments

4 changed files

mrnugget

pr closed time in 12 hours

issue closedsourcegraph/sourcegraph

Implement the "processing?" checks in CloseCampaign/DeleteCampaign

The question is: do we really need the check? If we make closing/detaching of changesets asynchronous, we don't need the check.

closed time in 12 hours

mrnugget

push eventsourcegraph/sourcegraph

Ryan Slade

commit sha 8436ad194ddb8624a2b7b3d5acfe54320ca10c02

usagestats: Use context for Redis connections (#12787)

view details

Thorsten Ball

commit sha ec1fc7ba53c2d29f38b7d5842d1f3619acd721b3

Remove noise and boilerplate from campaigns.TestService (#12783)

view details

Thorsten Ball

commit sha 99b5c994e1de9d91c002bdcfc1f7592c2c8ffa52

Fix deletion of expired ChangesetSpecs (#12780) The comments in the code explain what was wrong before: * We tried to delete `CampaignSpecs` before the `ChangesetSpecs`, which would lead to a foreign key constraint violation. * We expired `ChangesetSpecs` that were still attached to a `Changeset`, either as the `PreviousSpec` or the `CurrentSpec`. This commit fixes both issues and fixes #12763.

view details

Erik Seliger

commit sha b4cbefee1953912a01c3d39d671c72e9168dcbdd

Properly return null for empty descriptions on labels (#12770)

view details

ᴜɴᴋɴᴡᴏɴ

commit sha ff5324335c279f777959a5c4dbee0b6f462040ae

external_services: add NoNamespace option to the List method (#12781)

view details

Stefan Hengl

commit sha 2101f23b9e9de8abf953d6e45d5d5c04388718f1

search: facilitate simple searches if globbing is active (#12766)

view details

Eric Fritz

commit sha 2e63de368851f7ce99f70afebd1ec55662c2a25b

workerutil: Make generic store (#12792)

view details

Erik Seliger

commit sha 34ef7c28ee718de52466f5cf603fbe67f771e0f9

Don't load twice if changeset is not scheduled for syncing (#12771) * Don't load twice if changeset is not scheduled for synching Before, an empty preloadedNextSyncAt was not distinguished from not preloaded, and hence we loaded the sync state twice. This fixes it by explicitly passing that a preload was performed. * Add tests for sync resolver

view details

Eric Fritz

commit sha 2e575f0dce79d45bc3d31696510a8ddfc2714660

codeintel: Add mocks for queue client (#12798)

view details

Erik Seliger

commit sha 56748daed9f0cee2b6e53eacac7d4b136f72274d

Add test for label resolver with empty description (#12790)

view details

Erik Seliger

commit sha 07b389060aa4086917b5d956d03e2645f718ee4c

Fix pagination for changesets (#12794) Also moves the calculation of the sync state to the nodes resolver because it's not needed for PageInfo.

view details

Eric Fritz

commit sha 70b5927e190ae26c9d37bbca50f065c243fc79e0

internal API proxy: Remove verb allowlist in gitservice proxy (#12797)

view details

Eric Fritz

commit sha c2159f2f4867ce4499027fabe831404e6d2a2bb6

codeintel: Add disable indexer flag (#12800)

view details

Rijnard van Tonder

commit sha f41a2adca8927c1cf6be8f23026d4abf96d2281f

search: perform same trailing paren heuristic as old parser in new parser (#12774)

view details

Juliana Peña

commit sha 0e600a5fb205b8a0951ebf31ef8d7bb307711074

search: clicking a search suggestion no longer appends filters (#12721) Search suggestions already come with all the existing filters added, so there is no need to add them to the URL when generating the link. Also fixes a small bug that caused filters to not be toggled properly (eg. after clicking on "lang:c++" and then on "lang:c", the result would be "++" instead of "lang:c++ lang:c". Also added unit tests and an integration test for these bugs.

view details

Rijnard van Tonder

commit sha c97612c821b3cd7b7c2a5bbd9debb3974b387ce4

search: fix panic calling getExactFilePatterns for unguarded glob flag (#12807)

view details

uwedeportivo

commit sha fecd01c17999f94a557f0a9692132795def788da

e2e: fix incorrect repo names (#12808)

view details

renovate[bot]

commit sha ac46965452fc3ddc0d9f2ce173e66bc0dd00417b

Update grafana/grafana Docker tag to v7.1.3 (#12652) Co-authored-by: Renovate Bot <bot@renovateapp.com>

view details

ᴜɴᴋɴᴡᴏɴ

commit sha fe51a59c6b48761fee9e57b10c48654f7ea87c88

external_services: use SQL to get distinct kinds (#12779)

view details

Renovate Bot

commit sha b07db53111858f6892dc797d8f308f0032067a8b

Update dependency @types/jest to v26.0.9

view details

push time in 12 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha a38720577a24124b8b89f918a4afbdb07e5ebba2

Allow listing a user's and org's campaigns (#12785) * Allow listing a user's and org's campaigns This fixes #12563. * Fix error handling in Resolver.Campaigns * Add Namespace options to CountCampaignsOpts * Add tests for listing a namespaces campaigns * Fix frontend build by adding camaigns to user literal

view details

push time in 12 hours

delete branch sourcegraph/sourcegraph

delete branch : mrn/campaigns-by-namespace

delete time in 12 hours

PR merged sourcegraph/sourcegraph

Reviewers
Allow listing a user's and org's campaigns

This fixes #12563.

@eseliger does it make sense to make the Namespace: graphql.ID parameter on campaigns() public? It would allow filtering campaigns by namespace on the main page. But I'm not sure whether we have a need for that right now.

@efritz @unknwon I saw that you two use this pattern of using EnterpriseResolvers to talk to them from the normal graphqlbackend — can you both take a look and see whether I did it correctly?

+310 -3

3 comments

13 changed files

mrnugget

pr closed time in 12 hours

issue closedsourcegraph/sourcegraph

Implement GraphQL API that allows to fetch campaigns by namespace

See this ticket for the API design: https://github.com/sourcegraph/sourcegraph/issues/12470

closed time in 12 hours

eseliger

pull request commentsourcegraph/sourcegraph

Allow listing a user's and org's campaigns

I added tests, the missing error-check that @eseliger pointed out, found and fixed a bug and thanks to the tests I realized that TotalCount was wrong.

Merging this now and if there's any post-merge feedback: feel free!

mrnugget

comment created time in 12 hours

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha 62858c35c4023dfb4041cb2911c19ad9924d9121

Fix frontend build by adding camaigns to user literal

view details

push time in 13 hours

Pull request review commentsourcegraph/sourcegraph

Allow listing a user's and org's campaigns

 func (r *Resolver) Campaigns(ctx context.Context, args *graphqlbackend.ListCampa 			opts.OnlyForAuthor = actor.UID 		} 	}++	if args.Namespace != nil {+		switch relay.UnmarshalKind(*args.Namespace) {+		case "User":+			err = relay.UnmarshalSpec(*args.Namespace, &opts.NamespaceUserID)

Fixed!

mrnugget

comment created time in 13 hours

pull request commentsourcegraph/sourcegraph

Add schema for importing existing changesets

Yep, let's get this merged. Shouldn't require any changes here, right? Since it only describes what the src-cli would need to transform (from importChangesets: to multiple changesetSpecs where ExternalID != "")

eseliger

comment created time in 13 hours

Pull request review commentsourcegraph/sourcegraph

Fix logic for To(Hidden)Changeset and remove fetch repo workaround

 type changesetResolver struct { 	specErr  error } +func NewChangesetResolverWithPreloadedNextSync(store *ee.Store, httpFactory *httpcli.Factory, changeset *campaigns.Changeset, repo *types.Repo, preloadedNextSyncAt *time.Time) *changesetResolver {

The fact that it's preloaded is redundant, since it's passed in :)

func NewChangesetResolverWithNextSync(store *ee.Store, httpFactory *httpcli.Factory, changeset *campaigns.Changeset, repo *types.Repo, NextSyncAt *time.Time) *changesetResolver {
eseliger

comment created time in 13 hours

Pull request review commentsourcegraph/sourcegraph

Fix logic for To(Hidden)Changeset and remove fetch repo workaround

 type changesetResolver struct {  	changeset *campaigns.Changeset -	attemptedPreloadRepo bool-	preloadedRepo        *types.Repo--	// cache repo because it's called more than once-	repoOnce sync.Once-	repo     *graphqlbackend.RepositoryResolver-	repoErr  error-	// The context with which we try to load the repository if it's not-	// preloaded. We need an extra field for that, because the-	// ToExternalChangeset/ToHiddenExternalChangeset methods cannot take a-	// context.Context without graphql-go panic'ing.-	repoCtx context.Context+	repo         *types.Repo

Can you add a comment here and explain that this needs to always be set and if it's not set then it's treated as "inaccessible"?

eseliger

comment created time in 13 hours

Pull request review commentsourcegraph/sourcegraph

Fix logic for To(Hidden)Changeset and remove fetch repo workaround

 func (r *changesetEventResolver) CreatedAt() graphqlbackend.DateTime { }  func (r *changesetEventResolver) Changeset(ctx context.Context) (graphqlbackend.ExternalChangesetResolver, error) {-	return &changesetResolver{-		store:       r.store,-		httpFactory: r.httpFactory,-		changeset:   r.changeset,-		repoCtx:     ctx,-	}, nil+	repo, err := db.Repos.Get(ctx, r.changeset.RepoID)+	if err != nil && !errcode.IsNotFound(err) {+		return nil, err+	}+	return NewChangesetResolver(r.store, r.httpFactory, r.changeset, repo), nil

Do we need this? changesetEventResolver is only instantiated in changesetEventsConnectionResolver and that one is instantiated here:

https://github.com/sourcegraph/sourcegraph/blob/2ba8369adeeddbe74337a437cdcb7ab1cd02164d/enterprise/internal/campaigns/resolvers/changeset.go#L358-L371

We could pass in the repo or even the *changesetResolver.

eseliger

comment created time in 13 hours

Pull request review commentsourcegraph/sourcegraph

Deduplicate logic for determining map of accessible repos

 func (s *Service) ApplyCampaign(ctx context.Context, opts ApplyCampaignOpts) (ca  	// We load all the repositories involved, checking for repository permissions 	// under the hood.-	repoIDs := make([]api.RepoID, 0, len(newChangesetSpecs)+len(changesets))-	for _, spec := range newChangesetSpecs {-		repoIDs = append(repoIDs, spec.RepoID)-	}-	for _, changeset := range changesets {-		repoIDs = append(repoIDs, changeset.RepoID)-	}-	accessibleReposByID, err := accessibleRepos(ctx, repoIDs)+	repoIDs := newChangesetSpecs.RepoIDs()+	repoIDs = append(repoIDs, changesets.RepoIDs()...)
	repoIDs := append(newChangesetSpecs.RepoIDs(), changesets.RepoIDs()...)
eseliger

comment created time in 13 hours

Pull request review commentsourcegraph/sourcegraph

Deduplicate logic for determining map of accessible repos

 func (c *Changeset) URL() (s string, err error) { 	} } +// RepoIDs is a slice of RepoIDs.+type RepoIDs []api.RepoID++// AccessibleRepos returns the subset of repositories for which the actor in+// ctx has read permissions.+func (rs RepoIDs) AccessibleRepos(ctx context.Context) (map[api.RepoID]*types.Repo, error) {+	// 🚨 SECURITY: We use db.Repos.GetByIDs to filter out repositories the+	// user doesn't have access to.+	accessibleRepos, err := db.Repos.GetByIDs(ctx, rs...)+	if err != nil {+		return nil, err+	}++	accessibleRepoIDs := make(map[api.RepoID]*types.Repo, len(accessibleRepos))+	for _, r := range accessibleRepos {+		accessibleRepoIDs[r.ID] = r+	}++	return accessibleRepoIDs, nil+}++// ChangesetSpecs is a slice of *ChangesetSpecs.+type ChangesetSpecs []*ChangesetSpec++// IDs returns the unique RepoIDs of all changeset specs in the slice.+func (cs ChangesetSpecs) RepoIDs() RepoIDs {+	repoIDMap := make(map[api.RepoID]struct{})+	for _, c := range cs {+		repoIDMap[c.RepoID] = struct{}{}+	}+	repoIDs := make(RepoIDs, 0)+	for id := range repoIDMap {+		repoIDs = append(repoIDs, id)+	}+	return repoIDs+}+

I like the new approach in the db package.

eseliger

comment created time in 13 hours

Pull request review commentsourcegraph/sourcegraph

Deduplicate logic for determining map of accessible repos

 func (s *repos) GetByIDs(ctx context.Context, ids ...api.RepoID) ([]*types.Repo, 	return s.getReposBySQL(ctx, true, q) } +func (s *repos) GetRepoIDsSet(ctx context.Context, ids ...api.RepoID) (map[api.RepoID]*types.Repo, error) {

Since the returned set is a mapping from api.RepoID to *types.Repo I think this name makes more sense:

func (s *repos) GetReposSetByIDs(ctx context.Context, ids ...api.RepoID) (map[api.RepoID]*types.Repo, error) {

Because we return a set of repos, not a set of repo IDs.

eseliger

comment created time in 13 hours

Pull request review commentsourcegraph/sourcegraph

Deduplicate logic for determining map of accessible repos

 func (s *repos) GetByIDs(ctx context.Context, ids ...api.RepoID) ([]*types.Repo, 	return s.getReposBySQL(ctx, true, q) } +func (s *repos) GetRepoIDsSet(ctx context.Context, ids ...api.RepoID) (map[api.RepoID]*types.Repo, error) {

Also: can you add a doc string?

eseliger

comment created time in 13 hours

push eventsourcegraph/sourcegraph

Ryan Slade

commit sha 8436ad194ddb8624a2b7b3d5acfe54320ca10c02

usagestats: Use context for Redis connections (#12787)

view details

Thorsten Ball

commit sha ec1fc7ba53c2d29f38b7d5842d1f3619acd721b3

Remove noise and boilerplate from campaigns.TestService (#12783)

view details

Thorsten Ball

commit sha 99b5c994e1de9d91c002bdcfc1f7592c2c8ffa52

Fix deletion of expired ChangesetSpecs (#12780) The comments in the code explain what was wrong before: * We tried to delete `CampaignSpecs` before the `ChangesetSpecs`, which would lead to a foreign key constraint violation. * We expired `ChangesetSpecs` that were still attached to a `Changeset`, either as the `PreviousSpec` or the `CurrentSpec`. This commit fixes both issues and fixes #12763.

view details

Erik Seliger

commit sha b4cbefee1953912a01c3d39d671c72e9168dcbdd

Properly return null for empty descriptions on labels (#12770)

view details

ᴜɴᴋɴᴡᴏɴ

commit sha ff5324335c279f777959a5c4dbee0b6f462040ae

external_services: add NoNamespace option to the List method (#12781)

view details

Stefan Hengl

commit sha 2101f23b9e9de8abf953d6e45d5d5c04388718f1

search: facilitate simple searches if globbing is active (#12766)

view details

Eric Fritz

commit sha 2e63de368851f7ce99f70afebd1ec55662c2a25b

workerutil: Make generic store (#12792)

view details

Erik Seliger

commit sha 34ef7c28ee718de52466f5cf603fbe67f771e0f9

Don't load twice if changeset is not scheduled for syncing (#12771) * Don't load twice if changeset is not scheduled for synching Before, an empty preloadedNextSyncAt was not distinguished from not preloaded, and hence we loaded the sync state twice. This fixes it by explicitly passing that a preload was performed. * Add tests for sync resolver

view details

Eric Fritz

commit sha 2e575f0dce79d45bc3d31696510a8ddfc2714660

codeintel: Add mocks for queue client (#12798)

view details

Erik Seliger

commit sha 56748daed9f0cee2b6e53eacac7d4b136f72274d

Add test for label resolver with empty description (#12790)

view details

Erik Seliger

commit sha 07b389060aa4086917b5d956d03e2645f718ee4c

Fix pagination for changesets (#12794) Also moves the calculation of the sync state to the nodes resolver because it's not needed for PageInfo.

view details

Eric Fritz

commit sha 70b5927e190ae26c9d37bbca50f065c243fc79e0

internal API proxy: Remove verb allowlist in gitservice proxy (#12797)

view details

Eric Fritz

commit sha c2159f2f4867ce4499027fabe831404e6d2a2bb6

codeintel: Add disable indexer flag (#12800)

view details

Rijnard van Tonder

commit sha f41a2adca8927c1cf6be8f23026d4abf96d2281f

search: perform same trailing paren heuristic as old parser in new parser (#12774)

view details

Juliana Peña

commit sha 0e600a5fb205b8a0951ebf31ef8d7bb307711074

search: clicking a search suggestion no longer appends filters (#12721) Search suggestions already come with all the existing filters added, so there is no need to add them to the URL when generating the link. Also fixes a small bug that caused filters to not be toggled properly (eg. after clicking on "lang:c++" and then on "lang:c", the result would be "++" instead of "lang:c++ lang:c". Also added unit tests and an integration test for these bugs.

view details

Rijnard van Tonder

commit sha c97612c821b3cd7b7c2a5bbd9debb3974b387ce4

search: fix panic calling getExactFilePatterns for unguarded glob flag (#12807)

view details

uwedeportivo

commit sha fecd01c17999f94a557f0a9692132795def788da

e2e: fix incorrect repo names (#12808)

view details

renovate[bot]

commit sha ac46965452fc3ddc0d9f2ce173e66bc0dd00417b

Update grafana/grafana Docker tag to v7.1.3 (#12652) Co-authored-by: Renovate Bot <bot@renovateapp.com>

view details

ᴜɴᴋɴᴡᴏɴ

commit sha fe51a59c6b48761fee9e57b10c48654f7ea87c88

external_services: use SQL to get distinct kinds (#12779)

view details

Renovate Bot

commit sha b07db53111858f6892dc797d8f308f0032067a8b

Update dependency @types/jest to v26.0.9

view details

push time in 13 hours

issue openedsourcegraph/sourcegraph

Unable to enqueue changeset to be reconciled while it's being reconciled

Status quo

In order to enqueue a changeset to be picked up by the reconciler, we set its reconciler_state column to queued.

The reconciler then dequeues the changeset by setting the reconciler_state to processing:

https://github.com/sourcegraph/sourcegraph/blob/74ad60c5b67a800804279e036f8c849e14fc02c9/internal/workerutil/dbworker/store/store.go#L298-L315

Once it's done, it sets the state to completed or errored.

Problem

We want to move the closing of changesets into the background (https://github.com/sourcegraph/sourcegraph/issues/12644) by setting a "please close this" boolean on the changeset and enqueuing the changeset so that the reconciler picks it up and closes it.

We also want to allow this while a changeset is being processed by the reconciler, because the reconciler could — in the future — always be processing a changeset and that shouldn't stop the user from marking a changeset as "to be closed". See https://github.com/sourcegraph/sourcegraph/pull/12782#issue-463915652 for more context.

But here is the problem: if we enqueue a changeset while it's being processed by setting reconciler_state = 'queued' then that value will simply be overwritten by the reconciler once it's done with the current processing.

In other words: we can't enqueue a changeset to be processed again while it's being processed.

That's due to the architecture of the underlying dbworker/store, which assumes that every job is a one-off job that ends in a terminal state and is not re-enqueued again.

Possible Solution 1

We split the reconciler_state up into two fields:

  • enqueued: bool
  • processing_state: string

In order to enqueue a changeset, we set enqueued = TRUE.

We then need to change the dbworker/store so that the selectCandidateQuery allows us to set these fields, like this (see the selectCandidateQuery above):


WITH candidate AS (
  SELECT {id} FROM %s
  WHERE
    -- This is new
    ({state} = 'queued' OR (enqueued IS TRUE)) AND
    ({process_after} IS NULL OR {process_after} <= NOW())
    %s
  ORDER BY %s
  FOR UPDATE SKIP LOCKED
  LIMIT 1
)
UPDATE %s
SET
  -- This is new
  enqueued = FALSE,
  {state} = 'processing',
  {started_at} = NOW()
WHERE {id} IN (SELECT {id} FROM candidate)
RETURNING {id}

We could then compute the Changeset.ReconcilerState() by looking at both fields.

And we could always set the enqueued, even while a changeset is being processed, because the field acts like a dirty bit that's only cleared when dequeued. If we set it to true again, while it's being processed, the reconciler will pick it up again after it's done processing.

Possible Solution 2

We could switch away from using the changesets table as re-usable jobs that are re-executed again and again and instead create a new reconciler_job whenever we want the reconciler to look at the changeset.

That would require another table, of course, and that we need to fetch the jobs whenever we want to check the status of a changeset. We could make that easier with a database view, though, in which we always join the changesets against the reconciler_jobs.

But then we'd have the problem that we create a new job while the old one is being processed and both would try to modify the changeset, so we'd need to lock the changeset.


I'm leaning towards solution 1.

created time in 14 hours

issue closedsourcegraph/sourcegraph

Design a GraphQL API that would allow us to show either the "Update campaign" or "Create campaign" button on the apply page

We need to way to tell whether the application of the given campaign spec on the apply page would update an existing campaign or create a new campaign.

Quoting from our Campaigns sync doc:

When the user runs src campaign apply, it prints a URL like /{users,organizations}/$NAMESPACE_NAME/campaigns/apply?spec=$CAMPAIGN_SPEC_ID. That page shows something slightly different…

  • if the apply would create a new campaign: displays the spec and a "Create campaign" button. This uses the createCampaign mutation to ensure that if another campaign with the same namespace+name was created after the user loaded the page, that it would error instead of clobbering that unintentionally.
  • if the apply would update an existing campaign: displays a short summary of the existing campaign (namespace+name, link, and something like ), shows the new campaign spec (no delta) [that is the changeset specs], and an "Update campaign" button. This uses the applyCampaign mutation with ensureCampaign to ensure that the "Update campaign" button in fact updates the intended campaign and does not clobber another campaign in case the existing one is deleted and another one with the same namespace+name is created after the user loads the page. Runs in the namespace, URL: /users/abc/campaigns/apply?specID

Reply to this ticket with the proposed API

closed time in 14 hours

mrnugget

issue commentsourcegraph/sourcegraph

Design a GraphQL API that would allow us to show either the "Update campaign" or "Create campaign" button on the apply page

Closing this because it's done! The API implementation of this is covered in https://github.com/sourcegraph/sourcegraph/issues/12469 but can also be tackled separately.

mrnugget

comment created time in 14 hours

Pull request review commentsourcegraph/sourcegraph

Deduplicate logic for determining map of accessible repos

 func (c *Changeset) URL() (s string, err error) { 	} } +// RepoIDs is a slice of RepoIDs.+type RepoIDs []api.RepoID++// AccessibleRepos returns the subset of repositories for which the actor in+// ctx has read permissions.+func (rs RepoIDs) AccessibleRepos(ctx context.Context) (map[api.RepoID]*types.Repo, error) {+	// 🚨 SECURITY: We use db.Repos.GetByIDs to filter out repositories the+	// user doesn't have access to.+	accessibleRepos, err := db.Repos.GetByIDs(ctx, rs...)+	if err != nil {+		return nil, err+	}++	accessibleRepoIDs := make(map[api.RepoID]*types.Repo, len(accessibleRepos))+	for _, r := range accessibleRepos {+		accessibleRepoIDs[r.ID] = r+	}++	return accessibleRepoIDs, nil+}++// ChangesetSpecs is a slice of *ChangesetSpecs.+type ChangesetSpecs []*ChangesetSpec++// IDs returns the unique RepoIDs of all changeset specs in the slice.+func (cs ChangesetSpecs) RepoIDs() RepoIDs {+	repoIDMap := make(map[api.RepoID]struct{})+	for _, c := range cs {+		repoIDMap[c.RepoID] = struct{}{}+	}+	repoIDs := make(RepoIDs, 0)+	for id := range repoIDMap {+		repoIDs = append(repoIDs, id)+	}+	return repoIDs+}+

Or, another idea: how about defining db.Repos.GetRepoIDsSet(ctx, rs...)?

eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Deduplicate logic for determining map of accessible repos

 func (c *Changeset) URL() (s string, err error) { 	} } +// RepoIDs is a slice of RepoIDs.+type RepoIDs []api.RepoID

Would api.RepoIDs be a better place for this definition? Then repo-updater/repos could also access it (here, for example: https://github.com/sourcegraph/sourcegraph/blob/56748daed9f0cee2b6e53eacac7d4b136f72274d/cmd/repo-updater/repos/types.go#L794-L801

eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Deduplicate logic for determining map of accessible repos

 func (c *Changeset) URL() (s string, err error) { 	} } +// RepoIDs is a slice of RepoIDs.+type RepoIDs []api.RepoID++// AccessibleRepos returns the subset of repositories for which the actor in+// ctx has read permissions.+func (rs RepoIDs) AccessibleRepos(ctx context.Context) (map[api.RepoID]*types.Repo, error) {+	// 🚨 SECURITY: We use db.Repos.GetByIDs to filter out repositories the+	// user doesn't have access to.+	accessibleRepos, err := db.Repos.GetByIDs(ctx, rs...)+	if err != nil {+		return nil, err+	}++	accessibleRepoIDs := make(map[api.RepoID]*types.Repo, len(accessibleRepos))+	for _, r := range accessibleRepos {+		accessibleRepoIDs[r.ID] = r+	}++	return accessibleRepoIDs, nil+}++// ChangesetSpecs is a slice of *ChangesetSpecs.+type ChangesetSpecs []*ChangesetSpec++// IDs returns the unique RepoIDs of all changeset specs in the slice.+func (cs ChangesetSpecs) RepoIDs() RepoIDs {+	repoIDMap := make(map[api.RepoID]struct{})+	for _, c := range cs {+		repoIDMap[c.RepoID] = struct{}{}+	}+	repoIDs := make(RepoIDs, 0)+	for id := range repoIDMap {+		repoIDs = append(repoIDs, id)+	}+	return repoIDs+}+

I like the RepoIDs type RepoIDs() method on ChangesetSpecs but I don't like that AccessibleRepos makes a database call. That's so easy to miss, especially when dealing with transaction.

Could we make that a function in enterprise/internal/campaigns? Like filterAccessibleRepos(cs.RepoIDs())?

eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Fix pagination for changesets

 func TestChangesetConnectionResolver(t *testing.T) { 		externalBranch:      "merged-pr", 		externalState:       campaigns.ChangesetExternalStateMerged, 		publicationState:    campaigns.ChangesetPublicationStatePublished,+		externalReviewState: campaigns.ChangesetReviewStatePending,+		ownedByCampaign:     campaign.ID,+		campaign:            campaign.ID,+	})+	changeset4 := createChangeset(t, ctx, store, testChangesetOpts{+		repo:                inaccessibleRepo.ID,+		externalServiceType: "github",+		externalID:          "987651",+		externalBranch:      "open-hidden-pr",+		externalState:       campaigns.ChangesetExternalStateOpen,+		publicationState:    campaigns.ChangesetPublicationStatePublished,+		externalReviewState: campaigns.ChangesetReviewStatePending, 		ownedByCampaign:     campaign.ID, 		campaign:            campaign.ID, 	})  	addChangeset(t, ctx, store, campaign, changeset1.ID) 	addChangeset(t, ctx, store, campaign, changeset2.ID) 	addChangeset(t, ctx, store, campaign, changeset3.ID)+	addChangeset(t, ctx, store, campaign, changeset4.ID)  	s, err := graphqlbackend.NewSchema(&Resolver{store: store}, nil, nil) 	if err != nil { 		t.Fatal(err) 	}  	campaignApiID := string(campaigns.MarshalCampaignID(campaign.ID))+	nodes := []apitest.Changeset{+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset1.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset2.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset3.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename: "HiddenExternalChangeset",+			ID:       string(marshalChangesetID(changeset4.ID)),+		},+	} -	input := map[string]interface{}{"campaign": campaignApiID}-	var response struct{ Node apitest.Campaign }-	apitest.MustExec(ctx, t, s, input, &response, queryChangesetConnection)--	wantChangesets := apitest.ChangesetConnection{-		Stats: apitest.ChangesetConnectionStats{-			Unpublished: 1,-			Open:        1,-			Merged:      1,-			Closed:      0,-			Total:       3,+	tests := []struct {+		First           int+		unsafeOpts      bool+		wantHasNextPage bool+		wantTotalCount  int+		wantOpen        int+		wantNodes       []apitest.Changeset+	}{+		{+			First:           1,+			wantHasNextPage: true,+			wantTotalCount:  4,+			wantOpen:        2,+			wantNodes:       nodes[:1],+		},+		{+			First:           2,+			wantHasNextPage: true,+			wantTotalCount:  4,+			wantOpen:        2,+			wantNodes:       nodes[:2],+		},+		{+			First:           3,+			wantHasNextPage: true,+			wantTotalCount:  4,+			wantOpen:        2,+			wantNodes:       nodes[:3],+		},

How wide would these be on a single line? 🤔

	{ First: 3, wantHasNextPage: true, wantTotalCount: 4, wantOpen: 2, wantNodes: nodes[:3] },
eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Fix pagination for changesets

 func TestChangesetConnectionResolver(t *testing.T) { 		externalBranch:      "merged-pr", 		externalState:       campaigns.ChangesetExternalStateMerged, 		publicationState:    campaigns.ChangesetPublicationStatePublished,+		externalReviewState: campaigns.ChangesetReviewStatePending,+		ownedByCampaign:     campaign.ID,+		campaign:            campaign.ID,+	})+	changeset4 := createChangeset(t, ctx, store, testChangesetOpts{+		repo:                inaccessibleRepo.ID,+		externalServiceType: "github",+		externalID:          "987651",+		externalBranch:      "open-hidden-pr",+		externalState:       campaigns.ChangesetExternalStateOpen,+		publicationState:    campaigns.ChangesetPublicationStatePublished,+		externalReviewState: campaigns.ChangesetReviewStatePending, 		ownedByCampaign:     campaign.ID, 		campaign:            campaign.ID, 	})  	addChangeset(t, ctx, store, campaign, changeset1.ID) 	addChangeset(t, ctx, store, campaign, changeset2.ID) 	addChangeset(t, ctx, store, campaign, changeset3.ID)+	addChangeset(t, ctx, store, campaign, changeset4.ID)  	s, err := graphqlbackend.NewSchema(&Resolver{store: store}, nil, nil) 	if err != nil { 		t.Fatal(err) 	}  	campaignApiID := string(campaigns.MarshalCampaignID(campaign.ID))+	nodes := []apitest.Changeset{+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset1.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset2.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset3.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename: "HiddenExternalChangeset",+			ID:       string(marshalChangesetID(changeset4.ID)),+		},+	} -	input := map[string]interface{}{"campaign": campaignApiID}-	var response struct{ Node apitest.Campaign }-	apitest.MustExec(ctx, t, s, input, &response, queryChangesetConnection)--	wantChangesets := apitest.ChangesetConnection{-		Stats: apitest.ChangesetConnectionStats{-			Unpublished: 1,-			Open:        1,-			Merged:      1,-			Closed:      0,-			Total:       3,+	tests := []struct {+		First           int+		unsafeOpts      bool
		useUnsafeOpts      bool
eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Fix pagination for changesets

 func (cs Changesets) IDs() []int64 { 	return ids } -// IDs returns the RepoIDs of all changesets in the slice.+// IDs returns the unique RepoIDs of all changesets in the slice. func (cs Changesets) RepoIDs() []api.RepoID {-	repoIDs := make([]api.RepoID, len(cs))-	for i, c := range cs {-		repoIDs[i] = c.RepoID+	repoIDMap := make(map[api.RepoID]struct{})+	for _, c := range cs {+		repoIDMap[c.RepoID] = struct{}{}+	}+	repoIDs := make([]api.RepoID, 0)

make([]foo, 0) is equivalent to []foo{} or var s []foo, so it doesn't make a lot of sense to use this form.

But since you know the capacity, you can do this:

	repoIDs := make([]api.RepoID, 0, len(repoIDMap))
eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Fix pagination for changesets

 func TestChangesetConnectionResolver(t *testing.T) { 		externalBranch:      "merged-pr", 		externalState:       campaigns.ChangesetExternalStateMerged, 		publicationState:    campaigns.ChangesetPublicationStatePublished,+		externalReviewState: campaigns.ChangesetReviewStatePending,+		ownedByCampaign:     campaign.ID,+		campaign:            campaign.ID,+	})+	changeset4 := createChangeset(t, ctx, store, testChangesetOpts{+		repo:                inaccessibleRepo.ID,+		externalServiceType: "github",+		externalID:          "987651",+		externalBranch:      "open-hidden-pr",+		externalState:       campaigns.ChangesetExternalStateOpen,+		publicationState:    campaigns.ChangesetPublicationStatePublished,+		externalReviewState: campaigns.ChangesetReviewStatePending, 		ownedByCampaign:     campaign.ID, 		campaign:            campaign.ID, 	})  	addChangeset(t, ctx, store, campaign, changeset1.ID) 	addChangeset(t, ctx, store, campaign, changeset2.ID) 	addChangeset(t, ctx, store, campaign, changeset3.ID)+	addChangeset(t, ctx, store, campaign, changeset4.ID)  	s, err := graphqlbackend.NewSchema(&Resolver{store: store}, nil, nil) 	if err != nil { 		t.Fatal(err) 	}  	campaignApiID := string(campaigns.MarshalCampaignID(campaign.ID))+	nodes := []apitest.Changeset{+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset1.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset2.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset3.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename: "HiddenExternalChangeset",+			ID:       string(marshalChangesetID(changeset4.ID)),+		},+	} -	input := map[string]interface{}{"campaign": campaignApiID}-	var response struct{ Node apitest.Campaign }-	apitest.MustExec(ctx, t, s, input, &response, queryChangesetConnection)--	wantChangesets := apitest.ChangesetConnection{-		Stats: apitest.ChangesetConnectionStats{-			Unpublished: 1,-			Open:        1,-			Merged:      1,-			Closed:      0,-			Total:       3,+	tests := []struct {+		First           int+		unsafeOpts      bool+		wantHasNextPage bool+		wantTotalCount  int+		wantOpen        int+		wantNodes       []apitest.Changeset+	}{+		{+			First:           1,+			wantHasNextPage: true,+			wantTotalCount:  4,+			wantOpen:        2,+			wantNodes:       nodes[:1],+		},+		{+			First:           2,+			wantHasNextPage: true,+			wantTotalCount:  4,+			wantOpen:        2,+			wantNodes:       nodes[:2],+		},+		{+			First:           3,+			wantHasNextPage: true,+			wantTotalCount:  4,+			wantOpen:        2,+			wantNodes:       nodes[:3],+		},+		{+			First:           4,+			wantHasNextPage: false,+			wantTotalCount:  4,+			wantOpen:        2,+			wantNodes:       nodes[:4],+		},+		{+			First:           1,+			unsafeOpts:      true,+			wantHasNextPage: true,+			// Expect only 3 changesets to be returned when an unsafe filter is applied.+			wantTotalCount: 3,+			wantOpen:       1,+			wantNodes:      nodes[:1], 		},-		TotalCount: 3,-		Nodes: []apitest.Changeset{-			{-				Typename:   "ExternalChangeset",-				ID:         string(marshalChangesetID(changeset1.ID)),-				Repository: apitest.Repository{Name: repo.Name},-			},-			{-				Typename:   "ExternalChangeset",-				ID:         string(marshalChangesetID(changeset2.ID)),-				Repository: apitest.Repository{Name: repo.Name},-			},-			{-				Typename:   "ExternalChangeset",-				ID:         string(marshalChangesetID(changeset3.ID)),-				Repository: apitest.Repository{Name: repo.Name},-			},+		{+			First:           2,+			unsafeOpts:      true,+			wantHasNextPage: true,+			wantTotalCount:  3,+			wantOpen:        1,+			wantNodes:       nodes[:2],+		},+		{+			First:           3,+			unsafeOpts:      true,+			wantHasNextPage: false,+			wantTotalCount:  3,+			wantOpen:        1,+			wantNodes:       nodes[:3], 		}, 	} -	if diff := cmp.Diff(wantChangesets, response.Node.Changesets); diff != "" {-		t.Fatalf("wrong changesets response (-want +got):\n%s", diff)+	reviewStatePending := campaigns.ChangesetReviewStatePending++	for _, tc := range tests {+		t.Run(fmt.Sprintf("Unsafe opts %t, first %d", tc.unsafeOpts, tc.First), func(t *testing.T) {+			var reviewState *campaigns.ChangesetReviewState+			if tc.unsafeOpts {+				reviewState = &reviewStatePending+			}+			input := map[string]interface{}{"campaign": campaignApiID, "first": int64(tc.First), "reviewState": reviewState}

Nitpick, but I'd do something like this:

			input := map[string]interface{}{"campaign": campaignApiID, "first": int64(tc.First)}
			if tc.unsafeOpts {
				input["reviewState"] = &reviewStatePending
			}
eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Fix pagination for changesets

 query($campaign: ID!){ 			repository { name } 			nextSyncAt           }-        }+          ... on HiddenExternalChangeset {+            id+			nextSyncAt+          }+		}+		pageInfo {+		  hasNextPage+		}

Oh no! :scream: Some whitespace/tabs accident happened here! 🚨

eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Fix pagination for changesets

 func TestChangesetConnectionResolver(t *testing.T) { 		externalBranch:      "merged-pr", 		externalState:       campaigns.ChangesetExternalStateMerged, 		publicationState:    campaigns.ChangesetPublicationStatePublished,+		externalReviewState: campaigns.ChangesetReviewStatePending,+		ownedByCampaign:     campaign.ID,+		campaign:            campaign.ID,+	})+	changeset4 := createChangeset(t, ctx, store, testChangesetOpts{+		repo:                inaccessibleRepo.ID,+		externalServiceType: "github",+		externalID:          "987651",+		externalBranch:      "open-hidden-pr",+		externalState:       campaigns.ChangesetExternalStateOpen,+		publicationState:    campaigns.ChangesetPublicationStatePublished,+		externalReviewState: campaigns.ChangesetReviewStatePending, 		ownedByCampaign:     campaign.ID, 		campaign:            campaign.ID, 	})  	addChangeset(t, ctx, store, campaign, changeset1.ID) 	addChangeset(t, ctx, store, campaign, changeset2.ID) 	addChangeset(t, ctx, store, campaign, changeset3.ID)+	addChangeset(t, ctx, store, campaign, changeset4.ID)  	s, err := graphqlbackend.NewSchema(&Resolver{store: store}, nil, nil) 	if err != nil { 		t.Fatal(err) 	}  	campaignApiID := string(campaigns.MarshalCampaignID(campaign.ID))+	nodes := []apitest.Changeset{+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset1.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset2.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename:   "ExternalChangeset",+			ID:         string(marshalChangesetID(changeset3.ID)),+			Repository: apitest.Repository{Name: repo.Name},+		},+		{+			Typename: "HiddenExternalChangeset",+			ID:       string(marshalChangesetID(changeset4.ID)),+		},+	} -	input := map[string]interface{}{"campaign": campaignApiID}-	var response struct{ Node apitest.Campaign }-	apitest.MustExec(ctx, t, s, input, &response, queryChangesetConnection)--	wantChangesets := apitest.ChangesetConnection{-		Stats: apitest.ChangesetConnectionStats{-			Unpublished: 1,-			Open:        1,-			Merged:      1,-			Closed:      0,-			Total:       3,+	tests := []struct {+		First           int
		firstParam           int
eseliger

comment created time in a day

pull request commentsourcegraph/sourcegraph

Fix "changesets processing?" checks in CloseCampaign/DeleteCampaign methods

I'll add this in a separate PR tomorrow then.

mrnugget

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Don't load twice if changeset is not scheduled for syncing

 func marshalDateTime(t testing.TB, ts time.Time) string { 		t.Fatal(err) 	} -	return string(bs)+	// Unquote the date time.+	return strings.ReplaceAll(string(bs), "\"", "")

Makes sense to me!

eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

workerutil: Make generic store

 type reconciler struct {  // HandlerFunc returns a workerutil.HandlerFunc that can be passed to a

Can you update these comments? I reference workerutils.* in a few comments in the campaigns packages.

efritz

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Add test for label resolver with empty description

 func TestChangesetResolver(t *testing.T) { 				{Type: "LabeledEvent", Item: &github.LabelEvent{ 					CreatedAt: now.Add(5 * time.Second), 					Label: github.Label{+						ID:          "label-event", 						Name:        "cool-label", 						Color:       "blue",-						Description: "the best label in town",+						Description: labelEventDescriptionText, 					}, 				}}, 			},+			Labels: struct{ Nodes []github.Label }{+				Nodes: []github.Label{+					{ID: "label-no-description", Name: "Bug", Color: "121212"},
					{ID: "label-no-description", Name: "no-description", Color: "121212"},
eseliger

comment created time in a day

Pull request review commentsourcegraph/sourcegraph

Allow listing a user's and org's campaigns

 func (r *Resolver) Campaigns(ctx context.Context, args *graphqlbackend.ListCampa 		return nil, err

The admin check? Yeah, I left that in until we tackle the issue completely. I still have another ticket (https://github.com/sourcegraph/sourcegraph/issues/10713) and PR (#11621) for that.

mrnugget

comment created time in a day

pull request commentsourcegraph/sourcegraph

Fix "changesets processing?" checks in CloseCampaign/DeleteCampaign methods

I think this is okay for now, but we should revisit.

Yep, see my PR description.

With the reconciler, it could be that a campaign is almost always processing, so hitting close at the right time can be hard.

Yes, theoretically, but, not right now. Right now the reconciler only processes changesets after a user action. It'll only be a problem once the reconciler processes changesets on its own, which we don't have yet.

Also, there's no cheap way to figure out if the campaign is processing and hence the UI would just show the button and fail if is processing (which is fine for the first release IMO).

Couldn't you query campaign { changesets(reconcilerState: "processing") { totalCount } }?

In addition, the resolver for closeCampaign doesn't lock the changesets table, so there could be new jobs spawned after the processing check as well I believe.

Jobs (which are changesets) are not spawned without user input: in order for this race condition to happen, another user needs to hit "close" while another user applies a new campaign spec. And even then: both actions use a transaction.

And we could fix that by checking whether a campaign is closed in ApplyCampaign. It would be easy to add and I think it makes sense, what do you think?

mrnugget

comment created time in a day

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha 99b5c994e1de9d91c002bdcfc1f7592c2c8ffa52

Fix deletion of expired ChangesetSpecs (#12780) The comments in the code explain what was wrong before: * We tried to delete `CampaignSpecs` before the `ChangesetSpecs`, which would lead to a foreign key constraint violation. * We expired `ChangesetSpecs` that were still attached to a `Changeset`, either as the `PreviousSpec` or the `CurrentSpec`. This commit fixes both issues and fixes #12763.

view details

push time in a day

delete branch sourcegraph/sourcegraph

delete branch : mrn/fix-spec-expiry

delete time in a day

PR merged sourcegraph/sourcegraph

Fix deletion of expired ChangesetSpecs

The comments in the code explain what was wrong before:

  • We tried to delete CampaignSpecs before the ChangesetSpecs, which would lead to a foreign key constraint violation.
  • We expired ChangesetSpecs that were still attached to a Changeset, either as the PreviousSpec or the CurrentSpec.

This commit fixes both issues and fixes #12763.

+125 -23

0 comment

5 changed files

mrnugget

pr closed time in a day

issue closedsourcegraph/sourcegraph

Do not expire the PreviousChangesetSpec of a changeset

We expire CampaignSpecs and ChangesetSpecs in repo-updater:

https://github.com/sourcegraph/sourcegraph/blob/402b61a52ba6a36480eff5e827e963c2482ea7f0/enterprise/cmd/repo-updater/main.go#L63-L68

When do we expire?

  • CampaignSpecs are expired when they're not attached to a Campaign anymore.
  • ChangesetSpecs are expired when they're not attached to a CampaignSpec.

But since we extended ApplyCampaign we now have a CurrentSpecID and a PreviousSpecID on Changesets and set those:

https://github.com/sourcegraph/sourcegraph/blob/402b61a52ba6a36480eff5e827e963c2482ea7f0/enterprise/internal/campaigns/service.go#L581-L583

That means ApplyCampaign can leave us in a state where a Changeset has a PreviousSpecID that points to a ChangesetSpec that is attached to a CampaignSpec that's not attached to a campaign.

What we need to do:

  • Continue to expire campaignSpec that are not attached to campaigns anymore.
  • But don't clean up the ChangesetSpecs if they're attached to a Changeset, either as a CurrentSpecID or a PreviousSpecID.

For that we need to find out whether the deletion of a campaign_spec also deletes the associated changeset_specs. if that's the case, we need to stop.

And then, in DeleteExpiredChangesetSpecs, we need to not delete ChangesetSpecs that are attached to a changeset.

closed time in a day

mrnugget

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha ec1fc7ba53c2d29f38b7d5842d1f3619acd721b3

Remove noise and boilerplate from campaigns.TestService (#12783)

view details

push time in a day

delete branch sourcegraph/sourcegraph

delete branch : mrn/simplify-service-test

delete time in a day

PR merged sourcegraph/sourcegraph

Remove noise and boilerplate from campaigns.TestService

Just something I ran into while working on the tests in another PR.

+13 -67

0 comment

1 changed file

mrnugget

pr closed time in a day

PR opened sourcegraph/sourcegraph

Reviewers
Allow listing a user's and org's campaigns

This fixes #12563.

@eseliger does it make sense to make the Namespace: graphql.ID parameter on campaigns() public? It would allow filtering campaigns by namespace on the main page. But I'm not sure whether we have a need for that right now.

@efritz @unknwon I saw that you two use this pattern of using EnterpriseResolvers to talk to them from the normal graphqlbackend — can you both take a look and see whether I did it correctly?

+116 -1

0 comment

9 changed files

pr created time in a day

create barnchsourcegraph/sourcegraph

branch : mrn/campaigns-by-namespace

created branch time in a day

issue openedsourcegraph/sourcegraph

Update campaigns documentation for new workflow

We still have a ton of TODOs in our updated documentation:

https://sourcegraph.com/search?q=repo:%5Egithub%5C.com/sourcegraph/sourcegraph%24+f:doc/user/campaigns+TODO&patternType=regexp

Not all of them are valid, but this ticket should serve as a placeholder for a "fix the TODOs"

created time in a day

PR opened sourcegraph/sourcegraph

Remove noise and boilerplate from campaigns.TestService

Just something I ran into while working on the tests in another PR.

+13 -67

0 comment

1 changed file

pr created time in a day

create barnchsourcegraph/sourcegraph

branch : mrn/simplify-service-test

created branch time in a day

issue commentsourcegraph/sourcegraph

Implement the "processing?" checks in CloseCampaign/DeleteCampaign

Left thoughts on the question in the ticket here: https://github.com/sourcegraph/sourcegraph/pull/12782#issue-463915652

mrnugget

comment created time in a day

PR opened sourcegraph/sourcegraph

Fix "changesets processing?" checks in CloseCampaign/DeleteCampaign methods

This fixes #12643 by fixing the checks and updating the methods to the new workflow model.

  1. DeleteCampaign won't have a check anymore, since it also doesn't accept the closeChangesets: bool parameter anymore. That's documented in the GraphQL API and we should make that visible in the UI.
  2. CloseCampaign now only returns an error if the user wants to close the open changesets and the changesets are processing.

Why do we return an error? Because if the user wants to close the open changesets, we can't guarantee that we close all of them if they're currently processing.

In the future, when we move the closing of changesets to the reconciler, we can revisit this. But even then it'll be hard to mark a changeset as closed and enqueue it for the reconciler while the reconciler is working on it. Since the reconciler would overwrite the the reconciler_state field. But that can be worked around by, say, introducing a to_be_closed field and a cron-like job enqueuing these changesets again and again until the field is false and the external_state is closed.

+129 -58

0 comment

4 changed files

pr created time in a day

create barnchsourcegraph/sourcegraph

branch : mrn/fix-processing-checks

created branch time in a day

push eventsourcegraph/sourcegraph

Thorsten Ball

commit sha cb84f976d7314f9936d866a114d695ef70204f7e

Fix deletion of expired ChangesetSpecs The comments in the code explain what was wrong before: * We tried to delete `CampaignSpecs` before the `ChangesetSpecs`, which would lead to a foreign key constraint violation. * We expired `ChangesetSpecs` that were still attached to a `Changeset`, either as the `PreviousSpec` or the `CurrentSpec`. This commit fixes both issues and fixes #12763.

view details

push time in 2 days

more