Skip to content
Merged
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
140 changes: 140 additions & 0 deletions .surface
Original file line number Diff line number Diff line change
Expand Up @@ -349,13 +349,15 @@ ARG basecamp profile set-default 00 <name>
ARG basecamp profile show 00 [name]
ARG basecamp project create 00 <name>
ARG basecamp project delete 00 <id>
ARG basecamp project logo set 00 <file>
ARG basecamp project show 00 <id>
ARG basecamp project trash 00 <id>
ARG basecamp project update 00 <id>
ARG basecamp project wallpaper mode 00 <fill|tile>
ARG basecamp project wallpaper set 00 <file>
ARG basecamp projects create 00 <name>
ARG basecamp projects delete 00 <id>
ARG basecamp projects logo set 00 <file>
ARG basecamp projects show 00 <id>
ARG basecamp projects trash 00 <id>
ARG basecamp projects update 00 <id>
Expand Down Expand Up @@ -1079,6 +1081,9 @@ CMD basecamp project
CMD basecamp project create
CMD basecamp project delete
CMD basecamp project list
CMD basecamp project logo
CMD basecamp project logo remove
CMD basecamp project logo set
CMD basecamp project show
CMD basecamp project trash
CMD basecamp project update
Expand All @@ -1090,6 +1095,9 @@ CMD basecamp projects
CMD basecamp projects create
CMD basecamp projects delete
CMD basecamp projects list
CMD basecamp projects logo
CMD basecamp projects logo remove
CMD basecamp projects logo set
CMD basecamp projects show
CMD basecamp projects trash
CMD basecamp projects update
Expand Down Expand Up @@ -13728,6 +13736,69 @@ FLAG basecamp project list --status type=string
FLAG basecamp project list --styled type=bool
FLAG basecamp project list --todolist type=string
FLAG basecamp project list --verbose type=count
FLAG basecamp project logo --account type=string
FLAG basecamp project logo --agent type=bool
FLAG basecamp project logo --cache-dir type=string
FLAG basecamp project logo --count type=bool
FLAG basecamp project logo --help type=bool
FLAG basecamp project logo --hints type=bool
FLAG basecamp project logo --ids-only type=bool
FLAG basecamp project logo --in type=string
FLAG basecamp project logo --jq type=string
FLAG basecamp project logo --json type=bool
FLAG basecamp project logo --markdown type=bool
FLAG basecamp project logo --md type=bool
FLAG basecamp project logo --no-hints type=bool
FLAG basecamp project logo --no-stats type=bool
FLAG basecamp project logo --profile type=string
FLAG basecamp project logo --project type=string
FLAG basecamp project logo --quiet type=bool
FLAG basecamp project logo --stats type=bool
FLAG basecamp project logo --styled type=bool
FLAG basecamp project logo --todolist type=string
FLAG basecamp project logo --verbose type=count
FLAG basecamp project logo remove --account type=string
FLAG basecamp project logo remove --agent type=bool
FLAG basecamp project logo remove --cache-dir type=string
FLAG basecamp project logo remove --count type=bool
FLAG basecamp project logo remove --help type=bool
FLAG basecamp project logo remove --hints type=bool
FLAG basecamp project logo remove --ids-only type=bool
FLAG basecamp project logo remove --in type=string
FLAG basecamp project logo remove --jq type=string
FLAG basecamp project logo remove --json type=bool
FLAG basecamp project logo remove --markdown type=bool
FLAG basecamp project logo remove --md type=bool
FLAG basecamp project logo remove --no-hints type=bool
FLAG basecamp project logo remove --no-stats type=bool
FLAG basecamp project logo remove --profile type=string
FLAG basecamp project logo remove --project type=string
FLAG basecamp project logo remove --quiet type=bool
FLAG basecamp project logo remove --stats type=bool
FLAG basecamp project logo remove --styled type=bool
FLAG basecamp project logo remove --todolist type=string
FLAG basecamp project logo remove --verbose type=count
FLAG basecamp project logo set --account type=string
FLAG basecamp project logo set --agent type=bool
FLAG basecamp project logo set --cache-dir type=string
FLAG basecamp project logo set --count type=bool
FLAG basecamp project logo set --help type=bool
FLAG basecamp project logo set --hints type=bool
FLAG basecamp project logo set --ids-only type=bool
FLAG basecamp project logo set --in type=string
FLAG basecamp project logo set --jq type=string
FLAG basecamp project logo set --json type=bool
FLAG basecamp project logo set --markdown type=bool
FLAG basecamp project logo set --md type=bool
FLAG basecamp project logo set --no-hints type=bool
FLAG basecamp project logo set --no-stats type=bool
FLAG basecamp project logo set --profile type=string
FLAG basecamp project logo set --project type=string
FLAG basecamp project logo set --quiet type=bool
FLAG basecamp project logo set --stats type=bool
FLAG basecamp project logo set --styled type=bool
FLAG basecamp project logo set --todolist type=string
FLAG basecamp project logo set --verbose type=count
FLAG basecamp project show --account type=string
FLAG basecamp project show --agent type=bool
FLAG basecamp project show --all type=bool
Expand Down Expand Up @@ -13970,6 +14041,69 @@ FLAG basecamp projects list --status type=string
FLAG basecamp projects list --styled type=bool
FLAG basecamp projects list --todolist type=string
FLAG basecamp projects list --verbose type=count
FLAG basecamp projects logo --account type=string
FLAG basecamp projects logo --agent type=bool
FLAG basecamp projects logo --cache-dir type=string
FLAG basecamp projects logo --count type=bool
FLAG basecamp projects logo --help type=bool
FLAG basecamp projects logo --hints type=bool
FLAG basecamp projects logo --ids-only type=bool
FLAG basecamp projects logo --in type=string
FLAG basecamp projects logo --jq type=string
FLAG basecamp projects logo --json type=bool
FLAG basecamp projects logo --markdown type=bool
FLAG basecamp projects logo --md type=bool
FLAG basecamp projects logo --no-hints type=bool
FLAG basecamp projects logo --no-stats type=bool
FLAG basecamp projects logo --profile type=string
FLAG basecamp projects logo --project type=string
FLAG basecamp projects logo --quiet type=bool
FLAG basecamp projects logo --stats type=bool
FLAG basecamp projects logo --styled type=bool
FLAG basecamp projects logo --todolist type=string
FLAG basecamp projects logo --verbose type=count
FLAG basecamp projects logo remove --account type=string
FLAG basecamp projects logo remove --agent type=bool
FLAG basecamp projects logo remove --cache-dir type=string
FLAG basecamp projects logo remove --count type=bool
FLAG basecamp projects logo remove --help type=bool
FLAG basecamp projects logo remove --hints type=bool
FLAG basecamp projects logo remove --ids-only type=bool
FLAG basecamp projects logo remove --in type=string
FLAG basecamp projects logo remove --jq type=string
FLAG basecamp projects logo remove --json type=bool
FLAG basecamp projects logo remove --markdown type=bool
FLAG basecamp projects logo remove --md type=bool
FLAG basecamp projects logo remove --no-hints type=bool
FLAG basecamp projects logo remove --no-stats type=bool
FLAG basecamp projects logo remove --profile type=string
FLAG basecamp projects logo remove --project type=string
FLAG basecamp projects logo remove --quiet type=bool
FLAG basecamp projects logo remove --stats type=bool
FLAG basecamp projects logo remove --styled type=bool
FLAG basecamp projects logo remove --todolist type=string
FLAG basecamp projects logo remove --verbose type=count
FLAG basecamp projects logo set --account type=string
FLAG basecamp projects logo set --agent type=bool
FLAG basecamp projects logo set --cache-dir type=string
FLAG basecamp projects logo set --count type=bool
FLAG basecamp projects logo set --help type=bool
FLAG basecamp projects logo set --hints type=bool
FLAG basecamp projects logo set --ids-only type=bool
FLAG basecamp projects logo set --in type=string
FLAG basecamp projects logo set --jq type=string
FLAG basecamp projects logo set --json type=bool
FLAG basecamp projects logo set --markdown type=bool
FLAG basecamp projects logo set --md type=bool
FLAG basecamp projects logo set --no-hints type=bool
FLAG basecamp projects logo set --no-stats type=bool
FLAG basecamp projects logo set --profile type=string
FLAG basecamp projects logo set --project type=string
FLAG basecamp projects logo set --quiet type=bool
FLAG basecamp projects logo set --stats type=bool
FLAG basecamp projects logo set --styled type=bool
FLAG basecamp projects logo set --todolist type=string
FLAG basecamp projects logo set --verbose type=count
FLAG basecamp projects show --account type=string
FLAG basecamp projects show --agent type=bool
FLAG basecamp projects show --all type=bool
Expand Down Expand Up @@ -21002,6 +21136,9 @@ SUB basecamp project
SUB basecamp project create
SUB basecamp project delete
SUB basecamp project list
SUB basecamp project logo
SUB basecamp project logo remove
SUB basecamp project logo set
SUB basecamp project show
SUB basecamp project trash
SUB basecamp project update
Expand All @@ -21013,6 +21150,9 @@ SUB basecamp projects
SUB basecamp projects create
SUB basecamp projects delete
SUB basecamp projects list
SUB basecamp projects logo
SUB basecamp projects logo remove
SUB basecamp projects logo set
SUB basecamp projects show
SUB basecamp projects trash
SUB basecamp projects update
Expand Down
11 changes: 8 additions & 3 deletions API-COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ Coverage of Basecamp 3 API endpoints. Source: [bc3-api/sections](https://github.

| Status | Sections | Endpoints |
|--------|----------|-----------|
| ✅ Implemented | 56 | 216 |
| ✅ Implemented | 57 | 218 |
| ⚠️ Blocked | 0 | 0 |
| ⏭️ Out of scope | 4 | 12 |
| **Total tracked** | **60** | **228** |
| **Total tracked** | **61** | **230** |

**216 of 216 tracked in-scope endpoints.** Moving and copying a recording
**218 of 218 tracked in-scope endpoints.** Uploading and removing a project's
logo (`PUT`/`DELETE /projects/:id/logo.json`, added by
[bc3#14174](https://github.com/basecamp/bc3/pull/14174) and shipped in
[SDK v0.30.0](https://github.com/basecamp/basecamp-sdk/releases/tag/v0.30.0))
land as `projects logo`. Moving and copying a recording
into another project (`POST /recordings/:id/moves.json`,
`POST /recordings/:id/copies.json`) and reading the background filing they
start (`GET /filings/:id.json`, also read on its own by `filings show`), modelled by
Expand Down Expand Up @@ -212,6 +216,7 @@ cannot faithfully cover at least one endpoint for a reason outside the CLI. A
|---------|-----------|-------------|--------|-------|----------|-------|
| **Core** |
| projects | 9 | `projects` | ✅ | BC4 | - | list, show, create, update, delete |
| projects (logo) | 2 | `projects logo` | ✅ | BC5 | - | set (`PUT /projects/:id/logo.json`, multipart `logo`), remove (`DELETE` on the same route; 204 with or without a logo). PNG, JPEG, GIF, WebP, AVIF, HEIF or HEIC up to 5 MB, checked before the upload using a local MIME hint; the server applies its shared image validations. People and agents who can edit the project may change it; others get 403. Project JSON carries `logo {url}` when one is attached, so `projects show` shows it. `GET /projects/:id/logo.json` only redirects to the image `logo.url` already names, so the CLI does not wrap it. Project-scoped via `--in`/`--project` |
| projects (wallpaper) | 2 | `projects wallpaper` | ✅ | BC5 | - | set (`PUT /projects/:id/wallpaper.json`, multipart `wallpaper` with an optional `mode`), remove (`DELETE` on the same route; 204 with or without a wallpaper). `mode` sends `wallpaper_mode` through the existing `PUT /projects/:id.json`, with the current name the SDK requires. PNG, JPEG, GIF, WebP, AVIF, HEIF or HEIC up to 10 MB, checked before the upload using a local MIME hint; the server applies its shared image validations. People and personal assistants follow project-edit permissions; shared agents are refused with 403. Project JSON carries `wallpaper {url, mode}` when one is attached, so `projects show` shows it. Project-scoped via `--in`/`--project` |
| todos | 12 | `todos`, `todo`, `done`, `reopen` | ✅ | BC4 | - | list, show, create, update, complete, uncomplete, position, copy (BC5: `steps` shown on `todos show`, plus `subtasks_count`/`subtasks_completed_count`/`subtasks_url`; edit via `subtasks`). `todos create --loose` creates on the to-do set, outside any list |
| subtasks | 8 | `subtasks` | ✅ | BC5 | - | list (`GET /recordings/:id/subtasks.json`, paginated), show, create (`POST` on the same list route), update (partial; `--no-due`/`--no-assignees` clear), complete/uncomplete (`POST`/`DELETE /subtasks/:id/completion.json`), move (`PUT /subtasks/:id/position.json`, 1-based), delete. Account-scoped flat routes (bc3#12659), so no `--in`. Parents are to-dos and cards only. Same `Kanban::Step` records as `card_table_steps` |
Expand Down
54 changes: 54 additions & 0 deletions e2e/projects.bats
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,57 @@ load test_helper
run basecamp projects wallpaper remove skyline.png
assert_failure
}


@test "projects logo without subcommand shows help" {
run basecamp projects logo
assert_success
assert_output_contains "COMMANDS"
assert_output_contains "set"
assert_output_contains "remove"
}

@test "projects logo set help names the formats and the size cap" {
run basecamp projects logo set --help
assert_success
assert_output_contains "PNG, JPEG, GIF, WebP, AVIF, HEIF, or HEIC files up to 5 MB"
}

@test "projects logo set without a file shows error" {
create_credentials
create_global_config '{"account_id": 99999, "project_id": 123}'

run basecamp projects logo set
assert_failure
assert_json_value '.error' '<file> required'
assert_json_value '.code' 'usage'
}

@test "projects logo set refuses an SVG" {
create_credentials
create_global_config '{"account_id": 99999, "project_id": 123}'
printf '<svg></svg>' > brand.svg

run basecamp projects logo set brand.svg
assert_failure
assert_json_value '.error' 'brand.svg: unsupported image type image/svg+xml (use PNG, JPEG, GIF, WebP, AVIF, HEIF, or HEIC)'
assert_json_value '.code' 'usage'
}

@test "projects logo set refuses a file over 5 MB" {
create_credentials
create_global_config '{"account_id": 99999, "project_id": 123}'
dd if=/dev/zero of=big.png bs=1 count=1 seek=5242880 2>/dev/null

run basecamp projects logo set big.png
assert_failure
assert_json_value '.error' 'big.png exceeds maximum logo size of 5 MB'
}

@test "projects logo remove takes no arguments" {
create_credentials
create_global_config '{"account_id": 99999, "project_id": 123}'

run basecamp projects logo remove brand.png
assert_failure
}
28 changes: 28 additions & 0 deletions e2e/smoke/smoke_projects_write.bats
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,34 @@ setup_file() {
touch "$BATS_FILE_TMPDIR/wallpaper_set"
}

@test "projects logo set uploads a logo without changing wallpaper" {
[[ -f "$BATS_FILE_TMPDIR/wallpaper_set" ]] || mark_unverifiable "No wallpaper set in prior test"
local id_file="$BATS_FILE_TMPDIR/created_project_id"
[[ -f "$id_file" ]] || mark_unverifiable "No project created in prior test"
local proj_id
proj_id=$(<"$id_file")
local png="$BATS_FILE_TMPDIR/logo.png"
printf '%s' 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==' |
base64 -d > "$png"

run_smoke basecamp projects logo set "$png" --project "$proj_id" --json
assert_success
assert_json_not_null '.data.logo.url'
assert_json_value '.data.wallpaper.mode' 'tile'
touch "$BATS_FILE_TMPDIR/logo_set"
}

@test "projects logo remove clears only the logo" {
[[ -f "$BATS_FILE_TMPDIR/logo_set" ]] || mark_unverifiable "No logo set in prior test"
local proj_id
proj_id=$(<"$BATS_FILE_TMPDIR/created_project_id")

run_smoke basecamp projects logo remove --project "$proj_id" --json
assert_success
assert_json_value '.data.logo' 'null'
assert_json_value '.data.wallpaper.mode' 'tile'
}

@test "projects wallpaper mode changes the layout" {
[[ -f "$BATS_FILE_TMPDIR/wallpaper_set" ]] || mark_unverifiable "No wallpaper set in prior test"
local proj_id
Expand Down
2 changes: 1 addition & 1 deletion internal/commands/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func CommandCategories() []CommandCategory {
{
Name: "Core Commands",
Commands: []CommandInfo{
{Name: "projects", Category: "core", Description: "Manage projects", Actions: []string{"list", "show", "create", "update", "delete", "wallpaper"}},
{Name: "projects", Category: "core", Description: "Manage projects", Actions: []string{"list", "show", "create", "update", "delete", "wallpaper", "logo"}},
{Name: "todos", Category: "core", Description: "Manage to-dos", Actions: []string{"list", "show", "create", "update", "complete", "uncomplete", "position", "copy", "trash", "archive", "restore"}},
{Name: "subtasks", Category: "core", Description: "Manage subtasks on to-dos and cards", Actions: []string{"list", "show", "create", "update", "complete", "uncomplete", "move", "delete"}},
{Name: "todolists", Category: "core", Description: "Manage to-do lists", Actions: []string{"list", "show", "create", "update", "position", "copy", "trash", "archive", "restore"}},
Expand Down
Loading
Loading