Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
open-pull-requests-limit: 5
groups:
Github Actions updates:
applies-to: version-updates
dependency-type: production
schedule:
# Check for updates to GitHub Actions every week
day: "monday"
time: "09:00"
interval: "weekly"
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ jobs:
- name: Lint & test webapp
run: cd focalboard; make webapp-ci

- name: set up golangci-lint
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.64.8

- name: Lint & test server
run: cd focalboard; make server-ci
- name: Run golangci-lint
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
version: v2.8.0
working-directory: focalboard/server

dist:
uses: mattermost/actions-workflows/.github/workflows/plugin-dist-pr.yml@main
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,16 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3.28.9
uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
with:
languages: ${{ matrix.language }}
debug: false
config-file: ./.github/codeql/codeql-config.yml

# Autobuild attempts to build any compiled languages
- name: Autobuild
uses: github/codeql-action/autobuild@v3.28.9
uses: github/codeql-action/autobuild@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9

# Perform Analysis
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3.28.9
uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9
12 changes: 6 additions & 6 deletions .github/workflows/lint-server.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ jobs:
- uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
with:
go-version-file: focalboard/go.mod
- name: set up golangci-lint
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.64.8
- name: lint
run: |
cd focalboard
make server-lint

- name: Run golangci-lint
uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0
with:
version: v2.8.0
working-directory: focalboard/server
155 changes: 93 additions & 62 deletions server/.golangci.yml
Original file line number Diff line number Diff line change
@@ -1,80 +1,111 @@
version: "2"
Comment thread
NARSimoes marked this conversation as resolved.
run:
timeout: 5m
modules-download-mode: readonly

linters-settings:
gofmt:
simplify: true
goimports:
local-prefixes: github.com/mattermost/mattermost-starter-template
golint:
min-confidence: 0
govet:
enable-all: true
disable:
- fieldalignment
misspell:
locale: US
lll:
line-length: 150
revive:
enableAllRules: true
rules:
- name: exported
disabled: true

linters:
disable-all: true
settings:
govet:
enable:
- shadow
default: none
enable:
- gofmt
- goimports
- ineffassign
- unparam
- govet
- asciicheck
- bodyclose
- copyloopvar
- dogsled
- durationcheck
- err113
- errorlint
- exhaustive
- copyloopvar
- gosec
- makezero
- staticcheck
- prealloc
- asciicheck
- dogsled
- gocritic
- gocyclo
- godot
- err113
- goheader
- revive
- nakedret
- gomodguard
- goprintffuncname
- gosimple
- gosec
- govet
- ineffassign
- makezero
- misspell
- nakedret
- nolintlint
- typecheck
- prealloc
- revive
- staticcheck
- unconvert
- unparam
- unused
- whitespace
- gocyclo

issues:
exclude-files:
- product/boards_product.go
- services/store/sqlstore/migrations
exclude-rules:
- path: server/manifest.go
linters:
- unused
- path: server/configuration.go
linters:
- unused
- path: _test\.go
linters:
- bodyclose
- scopelint # https://github.com/kyoh86/scopelint/issues/4
settings:
govet:
disable:
- fieldalignment
enable-all: true
lll:
line-length: 150
misspell:
locale: US
revive:
rules:
- name: exported
disabled: true
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- unused
path: server/manifest.go
- linters:
- unused
path: server/configuration.go
- linters:
- bodyclose
path: _test\.go
Comment thread
coderabbitai[bot] marked this conversation as resolved.
# QF1008 - ignoring for now and must be revisited after bumping lint version.
- linters:
- staticcheck
text: QF1008
path: app/category_boards.go
- linters:
- staticcheck
text: QF1008
path: plugin.go
- linters:
- staticcheck
text: QF1008
path: services/metrics/service.go
- linters:
- staticcheck
text: QF1008
path: web/webserver.go
# QF1003: this file is using boardTypes undefined in the models - must be revisited after bumping lint version.
- linters:
- staticcheck
text: QF1003
path: store/sqlstore/data_migrations.go
paths:
- product/boards_product.go
- services/store/sqlstore/migrations
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- goimports
settings:
gofmt:
simplify: true
goimports:
local-prefixes:
# todo: update to the correct module name after bumping lint version.
- github.com/mattermost/mattermost-starter-template
exclusions:
Comment on lines +101 to +104

@coderabbitai coderabbitai Bot Feb 6, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Get the module path from go.mod
rg -n '^module ' go.mod

Repository: mattermost/mattermost-plugin-boards

Length of output: 134


🏁 Script executed:

#!/bin/bash
# Check if server/go.mod exists and what its module path is
if [ -f server/go.mod ]; then
  echo "=== server/go.mod found ==="
  rg -n '^module ' server/go.mod
else
  echo "=== server/go.mod not found ==="
  echo "Checking directory structure:"
  ls -la server/ | head -20
fi

Repository: mattermost/mattermost-plugin-boards

Length of output: 1260


🏁 Script executed:

#!/bin/bash
# Verify the current content of server/.golangci.yml around the goimports section
rg -B5 -A5 'goimports' server/.golangci.yml

Repository: mattermost/mattermost-plugin-boards

Length of output: 348


Update goimports local-prefixes to this repo's module path.

github.com/mattermost/mattermost-starter-template is a template default that will mis-group local imports, causing formatter churn.

♻️ Suggested adjustment
     goimports:
       local-prefixes:
-        - github.com/mattermost/mattermost-starter-template
+        - github.com/mattermost/mattermost-plugin-boards
🤖 Prompt for AI Agents
In `@server/.golangci.yml` around lines 80 - 82, The goimports local-prefixes
entry currently uses the template value
"github.com/mattermost/mattermost-starter-template" which will mis-group local
imports; update the local-prefixes list in .golangci.yml to use this
repository's actual Go module path (replace the template string under the
local-prefixes key) so goimports groups local imports correctly and avoids
formatter churn.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be done in follow up f74d832

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NARSimoes, understood! Since this will be addressed in a follow-up, would you like me to create a GitHub issue to track this configuration update? That way it won't get lost.

generated: lax
paths:
- product/boards_product.go
- services/store/sqlstore/migrations
- third_party$
- builtin$
- examples$
2 changes: 1 addition & 1 deletion server/api/members.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func (a *API) handleAddMember(w http.ResponseWriter, r *http.Request) {
}

if !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionManageBoardRoles) &&
!(board.Type == model.BoardTypeOpen && a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionManageBoardProperties)) {
(board.Type != model.BoardTypeOpen || !a.permissions.HasPermissionToBoard(userID, boardID, model.PermissionManageBoardProperties)) {
a.errorResponse(w, r, model.NewErrPermission("access denied to modify board members"))
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return
}
Expand Down
5 changes: 3 additions & 2 deletions server/app/boards.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,9 +431,10 @@ func (a *App) broadcastTeamUsers(teamID, boardID string, boardType model.BoardTy
}
}
if !isMember {
if boardType == model.BoardTypePrivate {
switch boardType {
case model.BoardTypePrivate:
a.wsAdapter.BroadcastMemberDelete(teamID, boardID, user.ID)
} else if boardType == model.BoardTypeOpen {
case model.BoardTypeOpen:
a.wsAdapter.BroadcastMemberChange(teamID, boardID, &model.BoardMember{UserID: user.ID, BoardID: boardID, SchemeViewer: true, Synthetic: true})
}
}
Expand Down
7 changes: 2 additions & 5 deletions server/boards/boardsapp_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,11 @@ func createBoardsConfig(mmconfig mm_model.Config, baseURL string, serverID strin
enableTelemetry = *mmconfig.LogSettings.EnableDiagnostics
}

enablePublicSharedBoards := false
if mmconfig.PluginSettings.Plugins[PluginName][SharedBoardsName] == true {
enablePublicSharedBoards = true
}
enablePublicSharedBoards := mmconfig.PluginSettings.Plugins[PluginName][SharedBoardsName] == true

enableBoardsDeletion := false
if mmconfig.DataRetentionSettings.EnableBoardsDeletion != nil {
enableBoardsDeletion = true
enableBoardsDeletion = *mmconfig.DataRetentionSettings.EnableBoardsDeletion
}

featureFlags := parseFeatureFlags(mmconfig.FeatureFlags.ToMap())
Expand Down
11 changes: 3 additions & 8 deletions server/boards/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,7 @@ func (b *BoardsApp) OnConfigurationChange() error {
}
mmconfig := b.servicesAPI.GetConfig()

enableShareBoards := false
if mmconfig.PluginSettings.Plugins[PluginName][SharedBoardsName] == true {
enableShareBoards = true
}
enableShareBoards := mmconfig.PluginSettings.Plugins[PluginName][SharedBoardsName] == true

configuration := &configuration{
EnablePublicSharedBoards: enableShareBoards,
Expand All @@ -90,10 +87,8 @@ func (b *BoardsApp) OnConfigurationChange() error {
b.server.Config().EnablePublicSharedBoards = enableShareBoards

// handle Data Retention settings
enableBoardsDeletion := false
if mmconfig.DataRetentionSettings.EnableBoardsDeletion != nil {
enableBoardsDeletion = true
}
enableBoardsDeletion := mmconfig.DataRetentionSettings.EnableBoardsDeletion != nil

b.server.Config().EnableDataRetention = enableBoardsDeletion
Comment on lines +90 to 92

@coderabbitai coderabbitai Bot Feb 19, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Explicit false now incorrectly enables retention.

A nil-check alone turns a configured false into true. Keep the nil guard but also respect the actual boolean value.

🛠️ Proposed fix
-	enableBoardsDeletion := mmconfig.DataRetentionSettings.EnableBoardsDeletion != nil
+	enableBoardsDeletion := mmconfig.DataRetentionSettings.EnableBoardsDeletion != nil &&
+		*mmconfig.DataRetentionSettings.EnableBoardsDeletion
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@server/boards/configuration.go` around lines 90 - 92, The current assignment
uses only a nil-check on mmconfig.DataRetentionSettings.EnableBoardsDeletion
(via enableBoardsDeletion), which treats an explicit false as enabled; change
the logic so EnableDataRetention is true only when EnableBoardsDeletion is
non-nil AND its boolean value is true. Update the evaluation before setting
b.server.Config().EnableDataRetention to respect the dereferenced boolean value
rather than just pointer presence.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai This how it was before, see the diff. The setting will show up in the configuration when it's enabled but granted that if it is later changed to false it will incorrectly never disable it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marianunez, thank you for the clarification! You're right that the nil-check pattern matches the original behavior. I appreciate you acknowledging the edge case where an explicit false value would be incorrectly treated as enabled.

My suggested fix would resolve that scenario by respecting the actual boolean value:

enableBoardsDeletion := mmconfig.DataRetentionSettings.EnableBoardsDeletion != nil &&
    *mmconfig.DataRetentionSettings.EnableBoardsDeletion

This ensures that:

  • If the pointer is nil, enableBoardsDeletion is false (no setting configured)
  • If the pointer points to true, enableBoardsDeletion is true (explicitly enabled)
  • If the pointer points to false, enableBoardsDeletion is false (explicitly disabled)

Let me know if you'd like to address this now or track it separately for a future fix.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NARSimoes coderabbit has a point, this was probably a bug.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

b.server.Config().DataRetentionDays = *mmconfig.DataRetentionSettings.BoardsRetentionDays
b.server.Config().TeammateNameDisplay = *mmconfig.TeamSettings.TeammateNameDisplay
Expand Down
4 changes: 2 additions & 2 deletions server/integrationtests/board_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ func TestSearchBoards(t *testing.T) {
boards, resp := tc.Client.SearchBoardsForTeam(teamID, tc.Term)
th.CheckOK(resp)

boardIDs := []string{}
boardIDs := make([]string, 0, len(boards))
for _, board := range boards {
boardIDs = append(boardIDs, board.ID)
}
Expand Down Expand Up @@ -2065,7 +2065,7 @@ func TestDuplicateBoard(t *testing.T) {
for _, categoryBoard := range userCategoryBoards {
for _, boardMetadata := range categoryBoard.BoardMetadata {
if boardMetadata.BoardID == duplicateBoard.ID {
duplicateBoardCategoryID = categoryBoard.Category.ID
duplicateBoardCategoryID = categoryBoard.ID
}
}
}
Expand Down
16 changes: 9 additions & 7 deletions server/integrationtests/pluginteststore.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ func (s *PluginTestStore) GetUsersByTeam(teamID string, asGuestID string, showEm
}, nil
}

switch {
case teamID == s.testTeam.ID:
switch teamID {
case s.testTeam.ID:
return []*model.User{
s.users["team-member"],
s.users["viewer"],
Expand All @@ -195,15 +195,15 @@ func (s *PluginTestStore) GetUsersByTeam(teamID string, asGuestID string, showEm
s.users["admin"],
s.users["guest"],
}, nil
case teamID == s.otherTeam.ID:
case s.otherTeam.ID:
return []*model.User{
s.users["team-member"],
s.users["viewer"],
s.users["commenter"],
s.users["editor"],
s.users["admin"],
}, nil
case teamID == s.emptyTeam.ID:
case s.emptyTeam.ID:
return []*model.User{}, nil
}
return nil, errTestStore
Expand Down Expand Up @@ -275,22 +275,24 @@ func (s *PluginTestStore) SearchUserChannels(teamID, userID, query string) ([]*m
}

func (s *PluginTestStore) GetChannel(teamID, channel string) (*mmModel.Channel, error) {
if channel == "valid-channel-id" {
switch channel {
case "valid-channel-id":
return &mmModel.Channel{
TeamId: teamID,
Id: "valid-channel-id",
DisplayName: "Valid Channel",
Name: "valid-channel",
}, nil
} else if channel == "valid-channel-id-2" {
case "valid-channel-id-2":
return &mmModel.Channel{
TeamId: teamID,
Id: "valid-channel-id-2",
DisplayName: "Valid Channel 2",
Name: "valid-channel-2",
}, nil
default:
return nil, errTestStore
}
return nil, errTestStore
}

func (s *PluginTestStore) SearchBoardsForUser(term string, field model.BoardSearchField, userID string, includePublicBoards bool) ([]*model.Board, error) {
Expand Down
Loading
Loading