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
31 changes: 24 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,23 @@ linear-release update --stage="in review" --release-version="1.2.0"

### CLI Options

| Option | Commands | Description |
| ------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--name` | `sync` | Custom release name. Defaults to short commit hash. |
| `--release-version` | `sync`, `complete`, `update` | Release version identifier. For `sync`, defaults to short commit hash. For `complete` and `update`, if omitted, targets the most recent started release. |
| `--stage` | `update` | Target deployment stage (required for `update`) |
| `--include-paths` | `sync` | Filter commits by changed file paths |
| `--json` | `sync`, `complete`, `update` | Output result as JSON |
| Option | Commands | Description |
| ------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--name` | `sync` | Custom release name. Continuous pipelines: used when creating a release. Scheduled pipelines: used only when `sync` creates a new release; existing release names are preserved. Ignored (with warning) for `complete` and `update`. |
| `--release-version` | `sync`, `complete`, `update` | Release version identifier. For `sync`, defaults to short commit hash. For `complete` and `update`, if omitted, targets the most recent started release. |
| `--stage` | `update` | Target deployment stage (required for `update`) |
| `--include-paths` | `sync` | Filter commits by changed file paths |
| `--json` | `sync`, `complete`, `update` | Output result as JSON |

### Command Targeting

| Command | With `--release-version` | Without `--release-version` |
| ---------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `sync` | Targets matching version or creates a release for that version | Continuous pipelines create a release with short SHA name/version. Scheduled pipelines use the current started/planned flow. |
| `update` | Updates that exact release version | Updates latest started release, or latest planned release if no started release exists |
| `complete` | Completes that exact release version | Completes latest started release |

For scheduled pipelines, prefer always passing `--release-version` in CI, especially when releases overlap.

### JSON Output

Expand Down Expand Up @@ -168,6 +178,13 @@ Path patterns can also be configured in your pipeline settings in Linear. If bot
4. **Detects pull request numbers** from commit messages (e.g., `Merge pull request #42`)
5. **Syncs to Linear** creating or updating the release with linked issues

## Troubleshooting

- **Unexpected release was updated/completed**: pass `--release-version` explicitly so the command does not target the latest started/planned release.
- **No release created by `sync`**: if no commits match the computed range (or path filters), `sync` returns `{"release":null}`.
- **Stage update fails**: verify stage name exactly. If stage names normalize to the same value, use the exact stage name to avoid ambiguity.
- **`--name` seems ignored**: `--name` only applies to `sync`; `complete` and `update` ignore it and print a warning.

## License

Licensed under the [MIT License](./LICENSE).
17 changes: 16 additions & 1 deletion src/args.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { parseCLIArgs } from "./args";
import { getCLIWarnings, parseCLIArgs } from "./args";

describe("parseCLIArgs", () => {
it("defaults command to sync when no positional given", () => {
Expand Down Expand Up @@ -83,4 +83,19 @@ describe("parseCLIArgs", () => {
it("throws on unknown flags (strict mode)", () => {
expect(() => parseCLIArgs(["--unknown-flag"])).toThrow();
});

it("returns warning when --name is used with update", () => {
const result = parseCLIArgs(["update", "--name", "Release 1.2.0"]);
expect(getCLIWarnings(result)).toEqual(['--name is ignored for "update" command; it only applies to "sync"']);
});

it("returns warning when --name is used with complete", () => {
const result = parseCLIArgs(["complete", "--name", "Release 1.2.0"]);
expect(getCLIWarnings(result)).toEqual(['--name is ignored for "complete" command; it only applies to "sync"']);
});

it("returns no warning when --name is used with sync", () => {
const result = parseCLIArgs(["sync", "--name", "Release 1.2.0"]);
expect(getCLIWarnings(result)).toEqual([]);
});
});
21 changes: 20 additions & 1 deletion src/args.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { parseArgs } from "node:util";

export function parseCLIArgs(argv: string[]) {
export type ParsedCLIArgs = {
command: string;
releaseName?: string;
releaseVersion?: string;
stageName?: string;
includePaths: string[];
jsonOutput: boolean;
};

export function parseCLIArgs(argv: string[]): ParsedCLIArgs {
const { values, positionals } = parseArgs({
args: argv,
options: {
Expand Down Expand Up @@ -28,3 +37,13 @@ export function parseCLIArgs(argv: string[]) {
jsonOutput: values.json ?? false,
};
}

export function getCLIWarnings(args: ParsedCLIArgs): string[] {
const warnings: string[] = [];

if (args.releaseName && args.command !== "sync") {
warnings.push(`--name is ignored for "${args.command}" command; it only applies to "sync"`);
}

return warnings;
}
10 changes: 8 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
PullRequestSource,
RepoInfo,
} from "./types";
import { parseCLIArgs } from "./args";
import { getCLIWarnings, parseCLIArgs } from "./args";
import { log, setStderr } from "./log";
import { pluralize } from "./util";
import { buildUserAgent } from "./user-agent";
Expand Down Expand Up @@ -77,6 +77,7 @@ try {
process.exit(1);
}
const { command, releaseName, releaseVersion, stageName, includePaths, jsonOutput } = parsedArgs;
const cliWarnings = getCLIWarnings(parsedArgs);
if (jsonOutput) {
setStderr(true);
}
Expand All @@ -85,11 +86,16 @@ const logEnvironmentSummary = () => {
log("Using access key authentication");

if (releaseName) {
log(`Using custom release name: ${releaseName}`);
if (command === "sync") {
log(`Using custom release name: ${releaseName}`);
}
}
if (releaseVersion) {
log(`Using custom release version: ${releaseVersion}`);
}
for (const warning of cliWarnings) {
log(`Warning: ${warning}`);
}

log(`Running in ${process.env.NODE_ENV === "development" ? "development" : "production"} mode`);
};
Expand Down