From 47fa96b9e948b01094eeb9cb1d58025c3088a63a Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Mon, 23 Feb 2026 14:01:51 +0100 Subject: [PATCH 1/4] fix: add options to mk for bucket creation --- src/lib/mk.ts | 30 ++++++++++++++++++++++++++++-- src/specs.yaml | 26 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/lib/mk.ts b/src/lib/mk.ts index 3706444..cd8fb0e 100644 --- a/src/lib/mk.ts +++ b/src/lib/mk.ts @@ -1,7 +1,7 @@ import { parseAnyPath } from '../utils/path.js'; import { getOption } from '../utils/options.js'; import { getStorageConfig } from '../auth/s3-client.js'; -import { createBucket, put } from '@tigrisdata/storage'; +import { createBucket, put, type StorageClass } from '@tigrisdata/storage'; export default async function mk(options: Record) { const pathString = getOption(options, ['path']); @@ -22,7 +22,33 @@ export default async function mk(options: Record) { if (!path) { // Create a bucket - const { error } = await createBucket(bucket, { config }); + const access = getOption(options, ['access', 'a', 'A']); + const enableSnapshots = getOption(options, [ + 'enableSnapshots', + 'enable-snapshots', + 's', + 'S', + ]); + const defaultTier = getOption(options, [ + 'defaultTier', + 'default-tier', + 't', + 'T', + ]); + const consistency = getOption(options, ['consistency', 'c', 'C']); + const region = getOption(options, ['region', 'r', 'R']); + + const { error } = await createBucket(bucket, { + defaultTier: (defaultTier ?? 'STANDARD') as StorageClass, + consistency: consistency === 'strict' ? 'strict' : 'default', + enableSnapshot: enableSnapshots === true, + access: access as 'public' | 'private', + region: + region !== 'global' && region !== undefined + ? region.split(',') + : undefined, + config, + }); if (error) { console.error(error.message); diff --git a/src/specs.yaml b/src/specs.yaml index 545e2d6..39239cd 100644 --- a/src/specs.yaml +++ b/src/specs.yaml @@ -243,6 +243,7 @@ commands: alias: create examples: - "tigris mk my-bucket" + - "tigris mk my-bucket --access public --region iad" - "tigris mk my-bucket/images/" - "tigris mk t3://my-bucket" messages: @@ -257,6 +258,31 @@ commands: - my-bucket/my-path - t3://my-bucket - t3://my-bucket/my-path + - name: access + description: Access level (only applies when creating a bucket) + alias: a + options: *access_options + default: private + - name: enable-snapshots + description: Enable snapshots for the bucket (only applies when creating a bucket) + alias: s + type: flag + default: false + - name: default-tier + description: Default storage tier (only applies when creating a bucket) + alias: t + options: *tier_options + default: STANDARD + - name: consistency + description: Consistency level (only applies when creating a bucket) + alias: c + options: *consistency_options + default: default + - name: region + description: Region (only applies when creating a bucket) + alias: r + options: *region_options + default: 'global' # touch - name: touch From 9422bd3d54acae19302818419cbb380df9bf31f4 Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Mon, 23 Feb 2026 14:07:43 +0100 Subject: [PATCH 2/4] chore: husky configs --- .husky/commit-msg | 3 --- .husky/pre-commit | 3 --- 2 files changed, 6 deletions(-) diff --git a/.husky/commit-msg b/.husky/commit-msg index c06ea44..36158d9 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npx --no-install commitlint --edit "$1" \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index ac781b7..13ae380 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,5 +1,2 @@ -#!/usr/bin/env sh -. "$(dirname -- "$0")/_/husky.sh" - npm run lint npm run format:check \ No newline at end of file From f262fc340390cb22c004c467ccd2dcdb99c2a620 Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Mon, 23 Feb 2026 16:35:04 +0100 Subject: [PATCH 3/4] docs: add options to mk command --- README.md | 239 ++++++++++++++++++++++++++++++++++++++++- scripts/update-docs.ts | 115 ++++++++++++++------ 2 files changed, 323 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index 361d21a..7170f20 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ Run `tigris help` to see all available commands, or `tigris help` for - `tigris cp ` - Copy files between local filesystem and Tigris, or between paths within Tigris. At least one side must be a remote t3:// path - `tigris mv ` - Move (rename) objects within Tigris. Both source and destination must be remote t3:// paths - `tigris rm ` - Remove a bucket, folder, or object from Tigris. A bare bucket name deletes the bucket itself +- `tigris stat [path]` - Show storage stats (no args), bucket info, or object metadata ### Authentication @@ -41,6 +42,7 @@ Run `tigris help` to see all available commands, or `tigris help` for - `tigris forks` - List and create forks. A fork is a writable copy-on-write clone of a bucket, useful for testing or branching data - `tigris snapshots` - List and take snapshots. A snapshot is a point-in-time, read-only copy of a bucket's state - `tigris objects` - Low-level object operations for listing, downloading, uploading, and deleting individual objects in a bucket +- `tigris iam` - Identity and Access Management - manage policies, users, and permissions --- @@ -67,12 +69,21 @@ tigris ls t3://my-bucket/prefix/ Create a bucket (bare name) or a folder inside a bucket (bucket/folder/ with trailing slash) ``` -tigris mk +tigris mk [flags] ``` +| Flag | Description | +|------|-------------| +| `-a, --access` | Access level (only applies when creating a bucket) | +| `-s, --enable-snapshots` | Enable snapshots for the bucket (only applies when creating a bucket) | +| `-t, --default-tier` | Default storage tier (only applies when creating a bucket) | +| `-c, --consistency` | Consistency level (only applies when creating a bucket) | +| `-r, --region` | Region (only applies when creating a bucket) | + **Examples:** ```bash tigris mk my-bucket +tigris mk my-bucket --access public --region iad tigris mk my-bucket/images/ tigris mk t3://my-bucket ``` @@ -152,6 +163,25 @@ tigris rm t3://my-bucket -f tigris rm "t3://my-bucket/logs/*.tmp" -f ``` +### `stat` + +Show storage stats (no args), bucket info, or object metadata + +``` +tigris stat [path] [flags] +``` + +| Flag | Description | +|------|-------------| +| `-f, --format` | Output format | + +**Examples:** +```bash +tigris stat +tigris stat t3://my-bucket +tigris stat t3://my-bucket/my-object.json +``` + ## Authentication ### `login` | `l` @@ -583,6 +613,7 @@ Low-level object operations for listing, downloading, uploading, and deleting in | `objects get` (g) | Download an object by key. Prints to stdout by default, or saves to a file with --output | | `objects put` (p) | Upload a local file as an object. Content-type is auto-detected from extension unless overridden | | `objects delete` (d) | Delete one or more objects by key from the given bucket | +| `objects set` (s) | Update settings on an existing object such as access level | #### `objects list` @@ -649,6 +680,212 @@ tigris objects delete my-bucket old-file.txt tigris objects delete my-bucket file-a.txt,file-b.txt ``` +#### `objects set` + +``` +tigris objects set [flags] +``` + +| Flag | Description | +|------|-------------| +| `-a, --access` | Access level | +| `-n, --new-key` | Rename the object to a new key | + +**Examples:** +```bash +tigris objects set my-bucket my-file.txt --access public +tigris objects set my-bucket my-file.txt --access private +``` + +### `iam` + +Identity and Access Management - manage policies, users, and permissions + +| Command | Description | +|---------|-------------| +| `iam policies` (p) | Manage IAM policies. Policies define permissions for access keys | +| `iam users` (u) | Manage organization users and invitations | + +#### `iam policies` | `p` + +Manage IAM policies. Policies define permissions for access keys + +| Command | Description | +|---------|-------------| +| `iam policies list` (l) | List all policies in the current organization | +| `iam policies get` (g) | Show details for a policy including its document and attached users. If no ARN provided, shows interactive selection | +| `iam policies create` (c) | Create a new policy with the given name and policy document. Document can be provided via file, inline JSON, or stdin | +| `iam policies edit` (e) | Update an existing policy's document. Document can be provided via file, inline JSON, or stdin. If no ARN provided, shows interactive selection | +| `iam policies delete` (d) | Delete a policy. If no ARN provided, shows interactive selection | + +##### `iam policies list` + +``` +tigris iam policies list [flags] +``` + +| Flag | Description | +|------|-------------| +| `-f, --format` | Output format (default: table) | + +**Examples:** +```bash +tigris iam policies list +``` + +##### `iam policies get` + +``` +tigris iam policies get [resource] [flags] +``` + +| Flag | Description | +|------|-------------| +| `-f, --format` | Output format (default: table) | + +**Examples:** +```bash +tigris iam policies get +tigris iam policies get arn:aws:iam::org_id:policy/my-policy +``` + +##### `iam policies create` + +``` +tigris iam policies create [flags] +``` + +| Flag | Description | +|------|-------------| +| `-d, --document` | Policy document (JSON file path or inline JSON). If omitted, reads from stdin | +| `--description` | Policy description | + +**Examples:** +```bash +tigris iam policies create my-policy --document policy.json +tigris iam policies create my-policy --document '{"Version":"2012-10-17","Statement":[...]}' +cat policy.json | tigris iam policies create my-policy +``` + +##### `iam policies edit` + +``` +tigris iam policies edit [resource] [flags] +``` + +| Flag | Description | +|------|-------------| +| `-d, --document` | New policy document (JSON file path or inline JSON). If omitted, reads from stdin | +| `--description` | Update policy description | + +**Examples:** +```bash +tigris iam policies edit --document policy.json +tigris iam policies edit arn:aws:iam::org_id:policy/my-policy --document policy.json +cat policy.json | tigris iam policies edit arn:aws:iam::org_id:policy/my-policy +``` + +##### `iam policies delete` + +``` +tigris iam policies delete [resource] +``` + +**Examples:** +```bash +tigris iam policies delete +tigris iam policies delete arn:aws:iam::org_id:policy/my-policy +``` + +#### `iam users` | `u` + +Manage organization users and invitations + +| Command | Description | +|---------|-------------| +| `iam users list` (l) | List all users and pending invitations in the organization | +| `iam users invite` (i) | Invite users to the organization by email | +| `iam users revoke-invitation` (ri) | Revoke pending invitations. If no invitation ID provided, shows interactive selection | +| `iam users update-role` (ur) | Update user roles in the organization. If no user ID provided, shows interactive selection | +| `iam users remove` (rm) | Remove users from the organization. If no user ID provided, shows interactive selection | + +##### `iam users list` + +``` +tigris iam users list [flags] +``` + +| Flag | Description | +|------|-------------| +| `-f, --format` | Output format (default: table) | + +**Examples:** +```bash +tigris iam users list +tigris iam users list --format json +``` + +##### `iam users invite` + +``` +tigris iam users invite [flags] +``` + +| Flag | Description | +|------|-------------| +| `-r, --role` | Role to assign to the invited user(s) (default: member) | + +**Examples:** +```bash +tigris iam users invite user@example.com +tigris iam users invite user@example.com --role admin +tigris iam users invite user1@example.com,user2@example.com +``` + +##### `iam users revoke-invitation` + +``` +tigris iam users revoke-invitation [resource] +``` + +**Examples:** +```bash +tigris iam users revoke-invitation +tigris iam users revoke-invitation invitation_id +tigris iam users revoke-invitation id1,id2,id3 +``` + +##### `iam users update-role` + +``` +tigris iam users update-role [resource] [flags] +``` + +| Flag | Description | +|------|-------------| +| `-r, --role` | Role(s) to assign (comma-separated). Each role pairs with the corresponding user ID. If one role is given, it applies to all users | + +**Examples:** +```bash +tigris iam users update-role --role admin +tigris iam users update-role user_id --role member +tigris iam users update-role id1,id2 --role admin +tigris iam users update-role id1,id2 --role admin,member +``` + +##### `iam users remove` + +``` +tigris iam users remove [resource] +``` + +**Examples:** +```bash +tigris iam users remove +tigris iam users remove user@example.com +tigris iam users remove user@example.com,user@example.net +``` + ## License MIT diff --git a/scripts/update-docs.ts b/scripts/update-docs.ts index 04e0d2e..2dd03b4 100644 --- a/scripts/update-docs.ts +++ b/scripts/update-docs.ts @@ -2,7 +2,7 @@ import { readFileSync, writeFileSync, existsSync } from 'fs'; import { join, dirname } from 'path'; import { fileURLToPath } from 'url'; import * as yaml from 'yaml'; -import type { OperationSpec, CommandSpec } from '../src/types.js'; +import type { CommandSpec } from '../src/types.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -16,14 +16,22 @@ interface Specs { } // Check if a command is implemented (has a corresponding .ts file without underscore prefix) -function isImplemented(cmdName: string, opName?: string): boolean { - const paths = opName - ? [join(libDir, cmdName, `${opName}.ts`), join(libDir, cmdName, opName, 'index.ts')] - : [join(libDir, `${cmdName}.ts`), join(libDir, cmdName, 'index.ts')]; - +function isImplemented(...parts: string[]): boolean { + const base = join(libDir, ...parts); + const paths = [base + '.ts', join(base, 'index.ts')]; return paths.some((p) => existsSync(p) && !p.includes('/_')); } +// Check if a command or any of its nested subcommands are implemented +function hasImplementation(cmd: CommandSpec, ...parentParts: string[]): boolean { + const parts = [...parentParts, cmd.name]; + if (isImplemented(...parts)) return true; + if (cmd.commands) { + return cmd.commands.some((sub) => hasImplementation(sub, ...parts)); + } + return false; +} + function getCommandUsage(cmd: CommandSpec): string { if (!cmd.arguments) return `tigris ${cmd.name}`; @@ -91,41 +99,79 @@ function generateCommandSection(cmd: CommandSpec): string { return lines.join('\n'); } -function generateResourceSection(cmd: CommandSpec, headerLevel: string = '###'): string { +function generateResourceSection( + cmd: CommandSpec, + parentPath: string[] = [], + headerLevel: string = '###' +): string { const lines: string[] = []; const aliasStr = cmd.alias ? ` | \`${cmd.alias}\`` : ''; + const commandPath = [...parentPath, cmd.name]; + const fullName = commandPath.join(' '); const subHeaderLevel = headerLevel === '###' ? '####' : '#####'; - lines.push(`${headerLevel} \`${cmd.name}\`${aliasStr}`); + lines.push(`${headerLevel} \`${fullName}\`${aliasStr}`); lines.push(''); lines.push(cmd.description); lines.push(''); - const implementedOps = cmd.operations?.filter((op) => isImplemented(cmd.name, op.name)) || []; + const subcommands = cmd.commands || []; + + // Check if subcommands have their own nested subcommands (e.g. iam -> policies -> list) + const hasNestedSubcommands = subcommands.some((sub) => sub.commands && sub.commands.length > 0); + + if (hasNestedSubcommands) { + // Parent resource (like iam) - recurse into sub-resources + const implementedSubs = subcommands.filter((sub) => hasImplementation(sub, ...commandPath)); + + if (implementedSubs.length > 0) { + lines.push('| Command | Description |'); + lines.push('|---------|-------------|'); + for (const sub of implementedSubs) { + const subAlias = sub.alias + ? ` (${Array.isArray(sub.alias) ? sub.alias[0] : sub.alias})` + : ''; + lines.push(`| \`${fullName} ${sub.name}\`${subAlias} | ${sub.description} |`); + } + lines.push(''); - if (implementedOps.length > 0) { - lines.push('| Command | Description |'); - lines.push('|---------|-------------|'); - for (const op of implementedOps) { - const opAlias = op.alias - ? ` (${Array.isArray(op.alias) ? op.alias[0] : op.alias})` - : ''; - lines.push(`| \`${cmd.name} ${op.name}\`${opAlias} | ${op.description} |`); + for (const sub of implementedSubs) { + lines.push(generateResourceSection(sub, commandPath, subHeaderLevel)); + } } - lines.push(''); + } else { + // Leaf resource - show implemented operations + const implementedOps = subcommands.filter((op) => isImplemented(...commandPath, op.name)); + + if (implementedOps.length > 0) { + lines.push('| Command | Description |'); + lines.push('|---------|-------------|'); + for (const op of implementedOps) { + const opAlias = op.alias + ? ` (${Array.isArray(op.alias) ? op.alias[0] : op.alias})` + : ''; + lines.push(`| \`${fullName} ${op.name}\`${opAlias} | ${op.description} |`); + } + lines.push(''); - for (const op of implementedOps) { - lines.push(generateOperationSection(cmd.name, op, subHeaderLevel)); + for (const op of implementedOps) { + lines.push(generateOperationSection(commandPath, op, subHeaderLevel)); + } } } return lines.join('\n'); } -function generateOperationSection(parentName: string, op: OperationSpec, headerLevel: string = '####'): string { +function generateOperationSection( + parentPath: string[], + op: CommandSpec, + headerLevel: string = '####' +): string { const lines: string[] = []; + const fullName = [...parentPath, op.name].join(' '); - lines.push(`${headerLevel} \`${parentName} ${op.name}\``); + lines.push(`${headerLevel} \`${fullName}\``); lines.push(''); const positionals = @@ -137,7 +183,7 @@ function generateOperationSection(parentName: string, op: OperationSpec, headerL lines.push('```'); lines.push( - `tigris ${parentName} ${op.name}${positionals.length ? ' ' + positionals.join(' ') : ''}${hasFlags ? ' [flags]' : ''}` + `tigris ${fullName}${positionals.length ? ' ' + positionals.join(' ') : ''}${hasFlags ? ' [flags]' : ''}` ); lines.push('```'); lines.push(''); @@ -191,12 +237,12 @@ function generateDocs(specs: Specs): string { } lines.push(''); - // Auth commands - check both direct implementation and operations + // Auth commands - check both direct implementation and subcommands const authCommandNames = ['login', 'logout', 'whoami', 'configure']; const authCommands = authCommandNames.filter((c) => { if (isImplemented(c)) return true; const cmd = specs.commands.find((s) => s.name === c); - return cmd?.operations?.some((op) => isImplemented(c, op.name)); + return cmd?.commands?.some((op) => isImplemented(c, op.name)); }); lines.push('### Authentication'); lines.push(''); @@ -209,10 +255,11 @@ function generateDocs(specs: Specs): string { lines.push(''); // Resource management - const resourceCommands = ['organizations', 'access-keys', 'credentials', 'buckets', 'forks', 'snapshots', 'objects']; + const resourceCommands = ['organizations', 'access-keys', 'credentials', 'buckets', 'forks', 'snapshots', 'objects', 'iam']; const implementedResources = resourceCommands.filter((c) => { const cmd = specs.commands.find((s) => s.name === c); - return cmd?.operations?.some((op) => isImplemented(c, op.name)); + if (!cmd) return false; + return hasImplementation(cmd); }); lines.push('### Resources'); @@ -243,8 +290,8 @@ function generateDocs(specs: Specs): string { for (const cmdName of authCommands) { const cmd = specs.commands.find((c) => c.name === cmdName); if (cmd) { - // Commands with operations use resource-style docs - if (cmd.operations?.some((op) => isImplemented(cmdName, op.name))) { + // Commands with subcommands use resource-style docs + if (cmd.commands?.some((op) => isImplemented(cmdName, op.name))) { lines.push(generateResourceSection(cmd)); } else { lines.push(generateCommandSection(cmd)); @@ -290,7 +337,7 @@ function generateDocs(specs: Specs): string { for (const cmdName of bucketRelated) { const cmd = specs.commands.find((c) => c.name === cmdName); if (cmd) { - lines.push(generateResourceSection(cmd, '####')); + lines.push(generateResourceSection(cmd, [], '####')); } } } @@ -303,6 +350,14 @@ function generateDocs(specs: Specs): string { } } + // IAM + if (implementedResources.includes('iam')) { + const iamCmd = specs.commands.find((c) => c.name === 'iam'); + if (iamCmd) { + lines.push(generateResourceSection(iamCmd)); + } + } + return lines.join('\n'); } From c695313a8bc7925f2bd50c6214d12d7b029410d2 Mon Sep 17 00:00:00 2001 From: A Ibrahim Date: Mon, 23 Feb 2026 18:18:50 +0100 Subject: [PATCH 4/4] chore: add --public shorthand for bucket create --- README.md | 2 ++ src/lib/buckets/create.ts | 13 ++++++++----- src/lib/mk.ts | 7 +++++-- src/specs.yaml | 6 ++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 7170f20..fc9c0ae 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,7 @@ tigris mk [flags] | Flag | Description | |------|-------------| | `-a, --access` | Access level (only applies when creating a bucket) | +| `--public` | Shorthand for --access public (only applies when creating a bucket) | | `-s, --enable-snapshots` | Enable snapshots for the bucket (only applies when creating a bucket) | | `-t, --default-tier` | Default storage tier (only applies when creating a bucket) | | `-c, --consistency` | Consistency level (only applies when creating a bucket) | @@ -467,6 +468,7 @@ tigris buckets create [name] [flags] | Flag | Description | |------|-------------| | `-a, --access` | Access level (default: private) | +| `--public` | Shorthand for --access public | | `-s, --enable-snapshots` | Enable snapshots for the bucket (default: false) | | `-t, --default-tier` | Choose the default tier for the bucket (default: STANDARD) | | `-c, --consistency` | Choose the consistency level for the bucket (default: default) | diff --git a/src/lib/buckets/create.ts b/src/lib/buckets/create.ts index ad015c8..9a6b2bc 100644 --- a/src/lib/buckets/create.ts +++ b/src/lib/buckets/create.ts @@ -24,9 +24,12 @@ export default async function create(options: Record) { // Extract all options from specs.yaml let name = getOption(options, ['name']); - let access = promptAll - ? undefined - : getOption(options, ['access', 'a', 'A']); + const isPublic = getOption(options, ['public']); + let access = isPublic + ? 'public' + : promptAll + ? undefined + : getOption(options, ['access', 'a', 'A']); let enableSnapshots = promptAll ? undefined : getOption(options, ['enable-snapshots', 's', 'S']); @@ -59,7 +62,7 @@ export default async function create(options: Record) { }); } - if (!access || promptAll) { + if ((!access || promptAll) && !isPublic) { const accessSpec = getArgumentSpec('buckets', 'access', 'create'); const choices = buildPromptChoices(accessSpec!); const defaultIndex = choices?.findIndex( @@ -164,7 +167,7 @@ export default async function create(options: Record) { defaultTier: (defaultTier ?? 'STANDARD') as StorageClass, consistency: consistency === 'strict' ? 'strict' : 'default', enableSnapshot: enableSnapshots === true, - access: access as 'public' | 'private', + access: (access ?? 'private') as 'public' | 'private', region: region !== 'global' && region !== undefined ? region.split(',') diff --git a/src/lib/mk.ts b/src/lib/mk.ts index cd8fb0e..3845818 100644 --- a/src/lib/mk.ts +++ b/src/lib/mk.ts @@ -22,7 +22,10 @@ export default async function mk(options: Record) { if (!path) { // Create a bucket - const access = getOption(options, ['access', 'a', 'A']); + const isPublic = getOption(options, ['public']); + const access = isPublic + ? 'public' + : getOption(options, ['access', 'a', 'A']); const enableSnapshots = getOption(options, [ 'enableSnapshots', 'enable-snapshots', @@ -42,7 +45,7 @@ export default async function mk(options: Record) { defaultTier: (defaultTier ?? 'STANDARD') as StorageClass, consistency: consistency === 'strict' ? 'strict' : 'default', enableSnapshot: enableSnapshots === true, - access: access as 'public' | 'private', + access: (access ?? 'private') as 'public' | 'private', region: region !== 'global' && region !== undefined ? region.split(',') diff --git a/src/specs.yaml b/src/specs.yaml index 39239cd..6e2609b 100644 --- a/src/specs.yaml +++ b/src/specs.yaml @@ -263,6 +263,9 @@ commands: alias: a options: *access_options default: private + - name: public + description: Shorthand for --access public (only applies when creating a bucket) + type: flag - name: enable-snapshots description: Enable snapshots for the bucket (only applies when creating a bucket) alias: s @@ -549,6 +552,9 @@ commands: alias: a options: *access_options default: private + - name: public + description: Shorthand for --access public + type: flag - name: enable-snapshots description: Enable snapshots for the bucket alias: s