Skip to content

Commit

Permalink
Add options to configure snapshot preid format
Browse files Browse the repository at this point in the history
  • Loading branch information
jakubmazanec committed Jun 3, 2022
1 parent d6bfcc5 commit 36dd7a0
Show file tree
Hide file tree
Showing 7 changed files with 188 additions and 30 deletions.
7 changes: 7 additions & 0 deletions .changeset/pink-eagles-agree.md
@@ -0,0 +1,7 @@
---
"@changesets/assemble-release-plan": minor
"@changesets/config": minor
"@changesets/types": minor
---

Adds two options to configure snapshot preid format: which character separates timestamp part from the rest of the preid, and whether the timestamp part comes first in the preid.
8 changes: 8 additions & 0 deletions docs/config-file-options.md
Expand Up @@ -156,3 +156,11 @@ You would specify our github changelog generator with:
```

For more details on these functions and information on how to write your own see [changelog-functions](./modifying-changelog-format.md)

## `snapshotTimestampSeparator` (`'-'` or `'.'`)

This option sets which character is used for separating timestamp part from the rest of the preid when doing [snapshot releases](./snapshot-releases.md), i.e. `0.0.0-bulbasaur-THE_TIME_YOU_DID_THIS` `vs 0.0.0-bulbasaur.THE_TIME_YOU_DID_THIS`. The default is `-`.

## `snapshotTimestampPosition` (`start` or `end`)

This option sets whether timestamp part comes at the start or at the end of the preid when doing [snapshot releases](./snapshot-releases.md), i.e. `0.0.0-bulbasaur-THE_TIME_YOU_DID_THIS` `vs 0.0.0-THE_TIME_YOU_DID_THIS-bulbasaur`. The default is `end`.
124 changes: 101 additions & 23 deletions packages/assemble-release-plan/src/index.test.ts
Expand Up @@ -31,31 +31,109 @@ describe("assemble-release-plan", () => {
});
});

it("should assemble release plan for basic setup with snapshot", () => {
let { releases } = assembleReleasePlan(
setup.changesets,
setup.packages,
defaultConfig,
undefined,
true
);
test.each([
{
snapshot: true,
snapshotTimestampSeparator: undefined,
snapshotTimestampPosition: undefined,
resultRegexp: /0\.0\.0-\d{14}/
},
{
snapshot: "foo",
snapshotTimestampSeparator: undefined,
snapshotTimestampPosition: undefined,
resultRegexp: /0\.0\.0-foo-\d{14}/
},
{
snapshot: "foo",
snapshotTimestampSeparator: "." as const,
snapshotTimestampPosition: undefined,
resultRegexp: /0\.0\.0-foo\.\d{14}/
},
{
snapshot: "foo",
snapshotTimestampSeparator: "-" as const,
snapshotTimestampPosition: undefined,
resultRegexp: /0\.0\.0-foo-\d{14}/
},
{
snapshot: "foo",
snapshotTimestampSeparator: undefined,
snapshotTimestampPosition: "start" as const,
resultRegexp: /0\.0\.0-\d{14}-foo/
},
{
snapshot: "foo",
snapshotTimestampSeparator: undefined,
snapshotTimestampPosition: "end" as const,
resultRegexp: /0\.0\.0-foo-\d{14}/
},
{
snapshot: "foo",
snapshotTimestampSeparator: "-" as const,
snapshotTimestampPosition: "start" as const,
resultRegexp: /0\.0\.0-\d{14}-foo/
},
{
snapshot: "foo",
snapshotTimestampSeparator: "-" as const,
snapshotTimestampPosition: "end" as const,
resultRegexp: /0\.0\.0-foo-\d{14}/
},
{
snapshot: "foo",
snapshotTimestampSeparator: "." as const,
snapshotTimestampPosition: "start" as const,
resultRegexp: /0\.0\.0-\d{14}\.foo/
},
{
snapshot: "foo",
snapshotTimestampSeparator: "." as const,
snapshotTimestampPosition: "end" as const,
resultRegexp: /0\.0\.0-foo\.\d{14}/
},
{
snapshot: true,
snapshotTimestampSeparator: undefined,
snapshotTimestampPosition: "start" as const,
resultRegexp: /0\.0\.0-\d{14}/
},
{
snapshot: true,
snapshotTimestampSeparator: undefined,
snapshotTimestampPosition: "end" as const,
resultRegexp: /0\.0\.0-\d{14}/
}
])(
"should assemble release plan for basic setup with %j",
({
snapshot,
snapshotTimestampSeparator,
snapshotTimestampPosition,
resultRegexp
}) => {
let config = { ...defaultConfig };

if (snapshotTimestampSeparator) {
config.snapshotTimestampSeparator = snapshotTimestampSeparator;
}

if (snapshotTimestampPosition) {
config.snapshotTimestampPosition = snapshotTimestampPosition;
}

expect(releases.length).toBe(1);
expect(/0\.0\.0-\d{14}/.test(releases[0].newVersion)).toBeTruthy();
});

it("should assemble release plan for basic setup with snapshot and tag", () => {
let { releases } = assembleReleasePlan(
setup.changesets,
setup.packages,
defaultConfig,
undefined,
"foo"
);
let { releases } = assembleReleasePlan(
setup.changesets,
setup.packages,
config,
undefined,
snapshot
);

expect(releases.length).toBe(1);
expect(/0\.0\.0-foo-\d{14}/.test(releases[0].newVersion)).toBeTruthy();
});
expect(releases.length).toBe(1);
expect(resultRegexp.test(releases[0].newVersion)).toBeTruthy();
}
);

it("should assemble release plan with multiple packages", () => {
setup.addChangeset({
Expand Down
32 changes: 26 additions & 6 deletions packages/assemble-release-plan/src/index.ts
Expand Up @@ -27,20 +27,36 @@ function getPreVersion(version: string) {
return preVersion;
}

function getSnapshotSuffix(snapshot?: string | boolean): string | undefined {
function getSnapshotSuffix(
snapshot: string | boolean | undefined,
timestampSeparator: "-" | ".",
timestampPosition: "start" | "end"
): string | undefined {
if (snapshot === undefined) {
return;
}

let dateAndTime = new Date()
const parts: string[] = [];
const timestamp = new Date()
.toISOString()
.replace(/\.\d{3}Z$/, "")
.replace(/[^\d]/g, "");
let tag = "";

if (typeof snapshot === "string") tag = `-${snapshot}`;
if (timestampPosition === "start") {
parts.push(timestamp);

return `${tag}-${dateAndTime}`;
if (typeof snapshot === "string") {
parts.push(snapshot);
}
} else {
if (typeof snapshot === "string") {
parts.push(snapshot);
}

parts.push(timestamp);
}

return `-${parts.join(timestampSeparator)}`;
}

function getNewVersion(
Expand Down Expand Up @@ -95,7 +111,11 @@ function assembleReleasePlan(
const preInfo = getPreInfo(changesets, packagesByName, config, preState);

// Caching the snapshot version here and use this if it is snapshot release
const snapshotSuffix = getSnapshotSuffix(snapshot);
const snapshotSuffix = getSnapshotSuffix(
snapshot,
config.snapshotTimestampSeparator,
config.snapshotTimestampPosition
);

// releases is, at this point a list of all packages we are going to releases,
// flattened down to one release per package, having a reference back to their
Expand Down
4 changes: 4 additions & 0 deletions packages/config/src/index.test.ts
Expand Up @@ -45,6 +45,8 @@ test("read reads the config", async () => {
updateInternalDependencies: "patch",
ignore: [],
bumpVersionsWithWorkspaceProtocolOnly: false,
snapshotTimestampSeparator: "-",
snapshotTimestampPosition: "end",
___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: {
onlyUpdatePeerDependentsWhenOutOfRange: false,
updateInternalDependents: "out-of-range",
Expand All @@ -62,6 +64,8 @@ let defaults = {
baseBranch: "master",
updateInternalDependencies: "patch",
ignore: [],
snapshotTimestampSeparator: "-",
snapshotTimestampPosition: "end",
___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: {
onlyUpdatePeerDependentsWhenOutOfRange: false,
updateInternalDependents: "out-of-range",
Expand Down
39 changes: 38 additions & 1 deletion packages/config/src/index.ts
Expand Up @@ -23,7 +23,9 @@ export let defaultWrittenConfig = {
access: "restricted",
baseBranch: "master",
updateInternalDependencies: "patch",
ignore: [] as ReadonlyArray<string>
ignore: [] as ReadonlyArray<string>,
snapshotTimestampSeparator: "-",
snapshotTimestampPosition: "end"
} as const;

function flatten<T>(arr: Array<T[]>): T[] {
Expand Down Expand Up @@ -315,6 +317,32 @@ export let parse = (json: WrittenConfig, packages: Packages): Config => {
}
}

if (
json.snapshotTimestampSeparator !== undefined &&
!["-", "."].includes(json.snapshotTimestampSeparator)
) {
messages.push(
`The \`snapshotTimestampSeparator\` option is set as ${JSON.stringify(
json.snapshotTimestampSeparator,
null,
2
)} but can only be '-' or '.'`
);
}

if (
json.snapshotTimestampPosition !== undefined &&
!["start", "end"].includes(json.snapshotTimestampPosition)
) {
messages.push(
`The \`snapshotTimestampPosition\` option is set as ${JSON.stringify(
json.snapshotTimestampPosition,
null,
2
)} but can only be 'start' or 'end'`
);
}

if (json.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH !== undefined) {
const {
onlyUpdatePeerDependentsWhenOutOfRange,
Expand Down Expand Up @@ -398,6 +426,15 @@ export let parse = (json: WrittenConfig, packages: Packages): Config => {
bumpVersionsWithWorkspaceProtocolOnly:
json.bumpVersionsWithWorkspaceProtocolOnly === true,

snapshotTimestampSeparator:
json.snapshotTimestampSeparator === undefined
? defaultWrittenConfig.snapshotTimestampSeparator
: json.snapshotTimestampSeparator,
snapshotTimestampPosition:
json.snapshotTimestampPosition === undefined
? defaultWrittenConfig.snapshotTimestampPosition
: json.snapshotTimestampPosition,

___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: {
onlyUpdatePeerDependentsWhenOutOfRange:
json.___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH === undefined ||
Expand Down
4 changes: 4 additions & 0 deletions packages/types/src/index.ts
Expand Up @@ -73,6 +73,8 @@ export type Config = {
ignore: ReadonlyArray<string>;
/** This is supposed to be used with pnpm's `link-workspace-packages: false` and Berry's `enableTransparentWorkspaces: false` */
bumpVersionsWithWorkspaceProtocolOnly?: boolean;
snapshotTimestampSeparator: "-" | ".";
snapshotTimestampPosition: "start" | "end";
___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH: Required<
ExperimentalOptions
>;
Expand All @@ -89,6 +91,8 @@ export type WrittenConfig = {
updateInternalDependencies?: "patch" | "minor";
ignore?: ReadonlyArray<string>;
bumpVersionsWithWorkspaceProtocolOnly?: boolean;
snapshotTimestampSeparator?: "-" | ".";
snapshotTimestampPosition?: "start" | "end";
___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH?: ExperimentalOptions;
};

Expand Down

0 comments on commit 36dd7a0

Please sign in to comment.