Skip to content
Draft
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
1 change: 1 addition & 0 deletions changes/add-support-software-category
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Added "πŸ›Ÿ Support" as a new default self-service software category.
1 change: 1 addition & 0 deletions cmd/maintained-apps/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ var allowedCategories = map[string]struct{}{
"Developer tools": {},
"Productivity": {},
"Security": {},
"Support": {},
"Utilities": {},
}

Expand Down
1 change: 1 addition & 0 deletions docs/Configuration/yaml-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ software:
- `Developer tools`: shown as **🧰 Developer tools**
- `Productivity`: shown as **πŸ–₯️ Productivity**
- `Security`: shown as **πŸ” Security**
- `Support`: shown as **πŸ›Ÿ Support**
- `Utilities`: shown as **πŸ› οΈ Utilities**
- `setup_experience` installs the software when hosts enroll (default: `false`). Learn more in the [setup experience guide](https://fleetdm.com/guides/setup-experience).

Expand Down
1 change: 1 addition & 0 deletions frontend/interfaces/software.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export type SoftwareCategory =
| "Developer tools"
| "Productivity"
| "Security"
| "Support"
| "Utilities";

export interface ISoftwarePackageStatus {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ export const CATEGORIES_ITEMS: ICategory[] = [
{ id: 3, label: "🧰 Developer tools", value: "Developer tools" },
{ id: 4, label: "πŸ–₯️ Productivity", value: "Productivity" },
{ id: 5, label: "πŸ” Security", value: "Security" },
{ id: 6, label: "πŸ› οΈ Utilities", value: "Utilities" },
{ id: 6, label: "πŸ›Ÿ Support", value: "Support" },
{ id: 7, label: "πŸ› οΈ Utilities", value: "Utilities" },
];

// Client-side category filter by name β€” both sides come from
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package tables

import (
"database/sql"

"github.com/pkg/errors"
)

func init() {
MigrationClient.AddMigration(Up_20260619120000, Down_20260619120000)
}

func Up_20260619120000(tx *sql.Tx) error {
// Add the new "πŸ›Ÿ Support" default self-service software category so it
// matches fleet.DefaultSelfServiceCategoryNames, which is used when seeding
// categories for newly created fleets.

// Global (team_id=0) default row. Pin both timestamps to the same constant
// the previous category migration used so the generated schema stays
// deterministic across repeated runs of make dump-test-schema.
if _, err := tx.Exec(`
INSERT IGNORE INTO software_categories (name, team_id, created_at, updated_at)
VALUES ('πŸ›Ÿ Support', 0, '2026-05-29 00:00:00', '2026-05-29 00:00:00')
`); err != nil {
return errors.Wrap(err, "inserting default Support software category")
}

// Give every existing fleet its own copy of the new default, matching the
// per-fleet backfill done when the categories were first scoped by team.
if _, err := tx.Exec(`
INSERT IGNORE INTO software_categories (name, team_id)
SELECT 'πŸ›Ÿ Support', t.id FROM teams t
`); err != nil {
return errors.Wrap(err, "backfilling Support category per fleet")
}

return nil
}

func Down_20260619120000(tx *sql.Tx) error {
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package tables

import (
"testing"
"time"

"github.com/stretchr/testify/require"
)

func TestUp_20260619120000(t *testing.T) {
db := applyUpToPrev(t)

// The global defaults seeded by earlier migrations should not yet include
// the new "πŸ›Ÿ Support" category.
var preCount int
require.NoError(t, db.Get(&preCount,
`SELECT COUNT(*) FROM software_categories WHERE team_id = 0 AND name = 'πŸ›Ÿ Support'`))
require.Equal(t, 0, preCount, "Support should not exist before this migration")

// Two existing fleets so the per-fleet backfill has rows to touch.
teamA := uint(execNoErrLastID(t, db, `INSERT INTO teams (name) VALUES (?)`, "team-a")) //nolint:gosec // dismiss G115
teamB := uint(execNoErrLastID(t, db, `INSERT INTO teams (name) VALUES (?)`, "team-b")) //nolint:gosec // dismiss G115

applyNext(t, db)

// The global default now exists exactly once at team_id=0.
type categoryRow struct {
ID uint `db:"id"`
Name string `db:"name"`
TeamID uint `db:"team_id"`
CreatedAt time.Time `db:"created_at"`
UpdatedAt time.Time `db:"updated_at"`
}
var globalRows []categoryRow
require.NoError(t, db.Select(&globalRows,
`SELECT id, name, team_id, created_at, updated_at FROM software_categories WHERE team_id = 0 AND name = 'πŸ›Ÿ Support'`))
require.Len(t, globalRows, 1, "Support should be added exactly once at team_id=0")
// Timestamps are pinned to a constant so the generated schema stays stable.
require.Equal(t, "2026-05-29", globalRows[0].CreatedAt.Format("2006-01-02"), "timestamps should be pinned for schema-dump stability")
require.Equal(t, "2026-05-29", globalRows[0].UpdatedAt.Format("2006-01-02"))

// Each existing fleet got its own copy.
for _, teamID := range []uint{teamA, teamB} {
var count int
require.NoError(t, db.Get(&count,
`SELECT COUNT(*) FROM software_categories WHERE team_id = ? AND name = 'πŸ›Ÿ Support'`, teamID))
require.Equal(t, 1, count, "fleet %d should have its own Support category", teamID)
Comment thread
allenhouchins marked this conversation as resolved.
}
}
8 changes: 4 additions & 4 deletions server/datastore/mysql/schema.sql

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions server/fleet/software.go
Original file line number Diff line number Diff line change
Expand Up @@ -899,6 +899,7 @@ var DefaultSelfServiceCategoryNames = []string{
"🧰 Developer tools",
"πŸ–₯️ Productivity",
"πŸ” Security",
"πŸ›Ÿ Support",
"πŸ› οΈ Utilities",
}

Expand All @@ -911,6 +912,7 @@ var LegacySoftwareCategoryNames = map[string]string{
"Developer tools": "🧰 Developer tools",
"Productivity": "πŸ–₯️ Productivity",
"Security": "πŸ” Security",
"Support": "πŸ›Ÿ Support",
"Utilities": "πŸ› οΈ Utilities",
}

Expand Down
Loading