Skip to content

Commit fd774dd

Browse files
committed
feat: add statuspage migration url name flag
1 parent 09988ba commit fd774dd

5 files changed

Lines changed: 59 additions & 1 deletion

File tree

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/flashcatcloud/flashduty-cli
33
go 1.25.1
44

55
require (
6-
github.com/flashcatcloud/flashduty-sdk v0.7.0
6+
github.com/flashcatcloud/flashduty-sdk v0.8.1-0.20260512032214-576d76bd987a
77
github.com/spf13/cobra v1.10.2
88
golang.org/x/term v0.42.0
99
gopkg.in/yaml.v3 v3.0.1

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
22
github.com/flashcatcloud/flashduty-sdk v0.7.0 h1:yPW8ghyHB60/34fz5sBITXhMWtbsm2mxYVFORgs+jpE=
33
github.com/flashcatcloud/flashduty-sdk v0.7.0/go.mod h1:dG4eJfdZaj4jNBMwEexbfK/3PmcIMhNeJ88L/DcZzUY=
4+
github.com/flashcatcloud/flashduty-sdk v0.8.1-0.20260512032214-576d76bd987a h1:nnMflbhcqVskLh22MaUpfXesqNegZ0uWUFd2sbenwT8=
5+
github.com/flashcatcloud/flashduty-sdk v0.8.1-0.20260512032214-576d76bd987a/go.mod h1:dG4eJfdZaj4jNBMwEexbfK/3PmcIMhNeJ88L/DcZzUY=
46
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
57
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
68
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=

internal/cli/status_page_migrate.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ func newStatusPageMigrateStructureCmd() *cobra.Command {
2525
var source string
2626
var sourcePageID string
2727
var sourceAPIKey string
28+
var urlName string
2829

2930
cmd := &cobra.Command{
3031
Use: "structure",
@@ -37,6 +38,7 @@ func newStatusPageMigrateStructureCmd() *cobra.Command {
3738
result, err := ctx.Client.StartStatusPageMigration(cmdContext(ctx.Cmd), &flashduty.StartStatusPageMigrationInput{
3839
SourceAPIKey: sourceAPIKey,
3940
SourcePageID: sourcePageID,
41+
URLName: urlName,
4042
})
4143
if err != nil {
4244
return err
@@ -50,6 +52,7 @@ func newStatusPageMigrateStructureCmd() *cobra.Command {
5052
cmd.Flags().StringVar(&source, "from", "", "Migration source provider (required)")
5153
cmd.Flags().StringVar(&sourcePageID, "source-page-id", "", "Source page ID in the provider (required)")
5254
cmd.Flags().StringVar(&sourceAPIKey, "api-key", "", "Source provider API key (required)")
55+
cmd.Flags().StringVar(&urlName, "url-name", "", "Optional URL name for a newly created Flashduty public status page; fails if the source page is already mapped to a different URL name")
5356
_ = cmd.MarkFlagRequired("from")
5457
_ = cmd.MarkFlagRequired("source-page-id")
5558
_ = cmd.MarkFlagRequired("api-key")

internal/cli/status_page_migrate_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ func TestCommandStatusPageMigrateStructureSendsSDKInput(t *testing.T) {
7777
if gotInput.SourcePageID != "src-1" {
7878
t.Errorf("SourcePageID = %q, want src-1", gotInput.SourcePageID)
7979
}
80+
if gotInput.URLName != "" {
81+
t.Errorf("URLName = %q, want empty", gotInput.URLName)
82+
}
8083
if !strings.Contains(out, "Job ID: job-1") {
8184
t.Errorf("missing job id in output:\n%s", out)
8285
}
@@ -85,6 +88,53 @@ func TestCommandStatusPageMigrateStructureSendsSDKInput(t *testing.T) {
8588
}
8689
}
8790

91+
func TestCommandStatusPageMigrateStructureSendsURLName(t *testing.T) {
92+
saveAndResetGlobals(t)
93+
94+
var gotInput *flashduty.StartStatusPageMigrationInput
95+
mock := &mockStatusPageMigrate{
96+
startStructure: func(_ context.Context, input *flashduty.StartStatusPageMigrationInput) (*flashduty.StartStatusPageMigrationOutput, error) {
97+
gotInput = input
98+
return &flashduty.StartStatusPageMigrationOutput{JobID: "job-url"}, nil
99+
},
100+
}
101+
newClientFn = func() (flashdutyClient, error) { return mock, nil }
102+
103+
_, err := execCommand("statuspage", "migrate", "structure",
104+
"--from", "atlassian",
105+
"--source-page-id", "src-1",
106+
"--api-key", "atlassian-secret",
107+
"--url-name", "customer-facing-status",
108+
)
109+
if err != nil {
110+
t.Fatalf("execCommand: %v", err)
111+
}
112+
113+
if gotInput == nil {
114+
t.Fatal("expected input to be captured")
115+
}
116+
if gotInput.URLName != "customer-facing-status" {
117+
t.Errorf("URLName = %q, want customer-facing-status", gotInput.URLName)
118+
}
119+
}
120+
121+
func TestCommandStatusPageMigrateStructureHelpDescribesURLNameBehavior(t *testing.T) {
122+
cmd := newStatusPageMigrateStructureCmd()
123+
flag := cmd.Flags().Lookup("url-name")
124+
if flag == nil {
125+
t.Fatal("expected --url-name flag to be registered")
126+
}
127+
128+
for _, want := range []string{
129+
"newly created Flashduty public status page",
130+
"already mapped to a different URL name",
131+
} {
132+
if !strings.Contains(flag.Usage, want) {
133+
t.Errorf("--url-name usage missing %q: %s", want, flag.Usage)
134+
}
135+
}
136+
}
137+
88138
func TestCommandStatusPageMigrateStructureRejectsUnsupportedSource(t *testing.T) {
89139
saveAndResetGlobals(t)
90140

skills/flashduty-statuspage/SKILL.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ flashduty statuspage migrate structure --from atlassian --source-page-id <id> --
117117
| `--from` | string | Source provider, currently only `atlassian` (**required**) |
118118
| `--source-page-id` | string | Page ID in the source provider (**required**) |
119119
| `--api-key` | string | Source provider API key (**required**) |
120+
| `--url-name` | string | Optional URL name for the newly created Flashduty public page. It is normalized with the same slug rules as page creation and only applies when the source page is not already mapped. If the source page already maps to a different Flashduty URL name, the command fails instead of changing the existing page. |
120121

121122
Returns a job ID plus the command to poll its status. Human output shows the new Flashduty `target_page_id` once the job reaches the `completed` phase — capture that for the subscriber migration.
122123

@@ -210,6 +211,7 @@ Import structure first, verify, then import subscribers.
210211
flashduty statuspage migrate structure \
211212
--from atlassian \
212213
--source-page-id page_atl_123 \
214+
--url-name customer-facing-status \
213215
--api-key "$ATLASSIAN_STATUSPAGE_API_KEY"
214216
# → captures Job ID: str_abc
215217

@@ -250,6 +252,7 @@ flashduty statuspage migrate status --job-id str_abc
250252
- **Page ID** (int) is the Flashduty status page primary key. **Change ID** (int) is the ID of an incident/maintenance within a page. Don't confuse them.
251253
- **Migration is async.** `migrate structure` and `migrate email-subscribers` return immediately with a job ID; the actual work happens on the backend.
252254
- **Two migration jobs, not one.** Structure + history run separately from subscribers. This is deliberate — subscriber import triggers verification emails, so operators verify content first.
255+
- **`--url-name` is create-only.** Use it to choose the public URL slug for a newly created Flashduty page. It does not rename an existing mapped target page; if the Atlassian page has already been migrated to another URL name, retry without `--url-name` or use the mapped page.
253256
- **Migration phases** for the structure job progress in order: `components``sections``history` (incidents + maintenances) → `templates`. The subscribers job has a single `subscribers` phase.
254257
- **Terminal statuses:** `completed`, `failed`, `cancelled`. Stop polling once any of these appears.
255258
- **`--notify` is subscriber-visible.** In `create-incident`, omit or set `--notify=false` for silent incidents; set `--notify` when you want an announcement.

0 commit comments

Comments
 (0)