Skip to content
Merged
12 changes: 10 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,18 @@ code in this repository.
**Development:**

- `yarn clean` - Clean compiled files and build artifacts
- `yarn generate:readme > &/dev/null` - Generate documentation from command
- `yarn generate:readme >/dev/null 2>&1` - Generate documentation from command
definitions

## Development best practices

- Follow the conventional commit format when writing commit messages
- Make sure to re-generate the documentation before each commit
- Before wrapping up a task, run this checklist in order:
1. `yarn lint`
2. `yarn compile`
3. `yarn test`
4. `yarn generate:readme >/dev/null 2>&1`

## Architecture Overview

Expand All @@ -40,7 +45,7 @@ container, etc.). Each command corresponds to a specific API operation.
- `BaseCommand` - Authenticated commands with API client setup
- `ListBaseCommand` - List operations with table formatting
- `RenderBaseCommand` - Single resource display
- `ExecRenderBaseCommand` - Long-running operations with progress
- `ExecRenderBaseCommand` - Run an `exec()` step and render its result with Ink
- `DeleteBaseCommand` - Delete operations with confirmation

**Context System:** Context management in `src/lib/context/` allows commands to
Expand Down Expand Up @@ -86,3 +91,6 @@ providers:
- Provide examples using the `static examples` property when useful
- Keep the command summary short; do not repeat the summary at the beginning of
the description
- Do not assume `ExecRenderBaseCommand` provides progress handling by itself; it
executes first and then renders, so use dedicated process/progress rendering
patterns when real-time progress output is required
62 changes: 62 additions & 0 deletions docs/stack.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ Manage container stacks
* [`mw stack ls`](#mw-stack-ls)
* [`mw stack ps`](#mw-stack-ps)
* [`mw stack rm [STACK-ID]`](#mw-stack-rm-stack-id)
* [`mw stack set-update-schedule STACK-ID SCHEDULE`](#mw-stack-set-update-schedule-stack-id-schedule)
* [`mw stack unset-update-schedule STACK-ID`](#mw-stack-unset-update-schedule-stack-id)
* [`mw stack up`](#mw-stack-up)

## `mw stack delete [STACK-ID]`
Expand Down Expand Up @@ -248,6 +250,66 @@ FLAG DESCRIPTIONS
scripts), you can use this flag to easily get the IDs of created resources for further processing.
```

## `mw stack set-update-schedule STACK-ID SCHEDULE`

Set the update schedule of a container stack

```
USAGE
$ mw stack set-update-schedule STACK-ID SCHEDULE [--token <value>] [-q] [--timezone <value>]

ARGUMENTS
STACK-ID ID of the stack
SCHEDULE Cron expression for the update schedule

FLAGS
-q, --quiet suppress process output and only display a machine-readable summary
--timezone=<value> Timezone for the update schedule (for example UTC or Europe/Berlin)

AUTHENTICATION FLAGS
--token=<value> API token to use for authentication (overrides environment and config file). NOTE: watch out that
tokens passed via this flag might be logged in your shell history.

DESCRIPTION
Set the update schedule of a container stack

FLAG DESCRIPTIONS
-q, --quiet suppress process output and only display a machine-readable summary

This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in
scripts), you can use this flag to easily get the IDs of created resources for further processing.
```


## `mw stack unset-update-schedule STACK-ID`

Unset the update schedule of a container stack

```
USAGE
$ mw stack unset-update-schedule STACK-ID [--token <value>] [-q]

ARGUMENTS
STACK-ID ID of the stack

FLAGS
-q, --quiet suppress process output and only display a machine-readable summary

AUTHENTICATION FLAGS
--token=<value> API token to use for authentication (overrides environment and config file). NOTE: watch out that
tokens passed via this flag might be logged in your shell history.

DESCRIPTION
Unset the update schedule of a container stack

FLAG DESCRIPTIONS
-q, --quiet suppress process output and only display a machine-readable summary

This flag controls if you want to see the process output or only a summary. When using mw non-interactively (e.g. in
scripts), you can use this flag to easily get the IDs of created resources for further processing.
```


## `mw stack up`

Deploys a docker-compose compatible file to a mittwald container stack
Expand Down
6 changes: 2 additions & 4 deletions release.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,8 @@ module.exports = {
[
"@semantic-release/commit-analyzer",
{
"releaseRules": [
{ "type": "chore", "scope": "deps", "release": "patch" },
]
}
releaseRules: [{ type: "chore", scope: "deps", release: "patch" }],
},
],
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
Expand Down
75 changes: 75 additions & 0 deletions src/commands/stack/set-update-schedule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { Args, Flags } from "@oclif/core";
import { ExecRenderBaseCommand } from "../../lib/basecommands/ExecRenderBaseCommand.js";
import assertSuccess from "../../lib/apiutil/assert_success.js";
import { ReactNode } from "react";
import {
makeProcessRenderer,
processFlags,
} from "../../rendering/process/process_flags.js";
import { Success } from "../../rendering/react/components/Success.js";

type Result = {
stackId: string;
};

export default class SetUpdateSchedule extends ExecRenderBaseCommand<
typeof SetUpdateSchedule,
Result
> {
static description = "Set the update schedule of a container stack";

static args = {
"stack-id": Args.string({
description: "ID of the stack",
required: true,
}),
schedule: Args.string({
description: "Cron expression for the update schedule",
required: true,
}),
};

static flags = {
...ExecRenderBaseCommand.baseFlags,
...processFlags,
timezone: Flags.string({
description:
"Timezone for the update schedule (for example UTC or Europe/Berlin)",
required: false,
}),
};

protected async exec(): Promise<Result> {
const p = makeProcessRenderer(this.flags, "Setting stack update schedule");

const stackId = this.args["stack-id"];

await p.runStep("updating stack schedule", async () => {
const response = await this.apiClient.container.setStackUpdateSchedule({
stackId,
data: {
updateSchedule: {
cron: this.args.schedule,
timezone: this.flags.timezone,
},
},
});

assertSuccess(response);
});

await p.complete(
<Success>
Update schedule for stack {stackId} was successfully set.
</Success>,
);

return { stackId };
}

protected render({ stackId }: Result): ReactNode {
if (this.flags.quiet) {
return stackId;
}
}
}
69 changes: 69 additions & 0 deletions src/commands/stack/unset-update-schedule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Args } from "@oclif/core";
import { ExecRenderBaseCommand } from "../../lib/basecommands/ExecRenderBaseCommand.js";
import assertSuccess from "../../lib/apiutil/assert_success.js";
import { ReactNode } from "react";
import {
makeProcessRenderer,
processFlags,
} from "../../rendering/process/process_flags.js";
import { Success } from "../../rendering/react/components/Success.js";

type Result = {
stackId: string;
};

export default class UnsetUpdateSchedule extends ExecRenderBaseCommand<
typeof UnsetUpdateSchedule,
Result
> {
static description = "Unset the update schedule of a container stack";

static args = {
"stack-id": Args.string({
description: "ID of the stack",
required: true,
}),
};

static flags = {
...ExecRenderBaseCommand.baseFlags,
...processFlags,
};

protected async exec(): Promise<Result> {
const p = makeProcessRenderer(
this.flags,
"Unsetting stack update schedule",
);

const stackId = this.args["stack-id"];

await p.runStep("removing stack schedule", async () => {
const response = await this.apiClient.container.setStackUpdateSchedule({
stackId,
data: {
updateSchedule: null as unknown as {
cron: string;
timezone?: string;
},
},
});

assertSuccess(response);
});

await p.complete(
<Success>
Update schedule for stack {stackId} was successfully removed.
</Success>,
);

return { stackId };
}

protected render({ stackId }: Result): ReactNode {
if (this.flags.quiet) {
return stackId;
}
}
}