Skip to content

Commit

Permalink
feat(prepare): add support for bumping package build number (#155)
Browse files Browse the repository at this point in the history
* feat(versionCode): implement version code generation logic

* docs: add description for new option item `useVersionCode`

* test(versionCode): add test case for bumping version codes

* refactor(versionCode): rename option `useVersionCode` to `updateBuildNumber`

* refactor(versionCode): make version code modifying logic to throw suitable error

* refactor(versionCode): now logging for `prepare` step will print actual version number

* docs(readme): update description for `updateBuildNumber` option

* docs(readme): update `updateBuildNumber` option description

Co-authored-by: Joshua Tang <joshuaystang@gmail.com>

---------

Co-authored-by: Joshua Tang <joshuaystang@gmail.com>
  • Loading branch information
async3619 and zeshuaro committed Jan 4, 2024
1 parent 8731bd2 commit 80bf1f5
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 11 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,11 @@ The following instructions are referenced from the [documentation](https://dart.

### Options

| Option | Description | Default |
| ------------ | -------------------------------------------------------------------------------------------------------------------- | ------- |
| `cli` | The `dart` or `flutter` CLI to use to publish the package to the registry. | `dart` |
| `publishPub` | Whether to publish the package to the registry. If set to `false`, the `pubspec.yaml` version will still be updated. | `true` |
| Option | Description | Default |
|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|
| `cli` | The `dart` or `flutter` CLI to use to publish the package to the registry. | `dart` |
| `publishPub` | Whether to publish the package to the registry. If set to `false`, the `pubspec.yaml` version will still be updated. | `true` |
| `updateBuildNumber` | Whether to write build number for every newly bumped version in `pubspec.yaml`. Note that the build number will always be increased by one. Learn more on [Flutter docs](https://docs.flutter.dev/deployment/android#updating-the-apps-version-number). | `false` |

### Examples

Expand Down
27 changes: 23 additions & 4 deletions src/prepare.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,46 @@
import { readFileSync, writeFileSync } from "fs";
import { PrepareContext } from "semantic-release";
import { parse } from "yaml";
import { PrepareContext } from "semantic-release";
import SemanticReleaseError from "@semantic-release/error";

import { Pubspec } from "./schemas.js";
import { PluginConfig } from "./types.js";
import { getConfig } from "./utils.js";

const PUBSPEC_PATH = "pubspec.yaml";

export const prepare = async (
_pluginConfig: PluginConfig,
pluginConfig: PluginConfig,
{ nextRelease: { version }, logger }: PrepareContext,
) => {
const { updateBuildNumber } = getConfig(pluginConfig);

const data = readFileSync(PUBSPEC_PATH, "utf-8");
const pubspec = Pubspec.parse(parse(data));
const pubspecVersionEscaped = pubspec.version.replace(
/[/\-\\^$*+?.()|[\]{}]/g,
"\\$&",
);

let nextVersion = version;
if (updateBuildNumber) {
const parts = pubspec.version.split("+");
const buildNumber = parts.length > 1 ? Number(parts[1]) : 0;

if (isNaN(buildNumber)) {
throw new SemanticReleaseError(
`Invalid build number: ${buildNumber} in ${pubspec.version}`,
);
}

nextVersion = `${version}+${buildNumber + 1}`;
}

const newData = data.replace(
new RegExp(`version:[ \t]+${pubspecVersionEscaped}`),
`version: ${version}`,
`version: ${nextVersion}`,
);

logger.log(`Writing version ${version} to ${PUBSPEC_PATH}`);
logger.log(`Writing version ${nextVersion} to ${PUBSPEC_PATH}`);
writeFileSync(PUBSPEC_PATH, newData);
};
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export type PluginConfig = {
cli: "dart" | "flutter";
publishPub: boolean;
updateBuildNumber: boolean;
};
1 change: 1 addition & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { PluginConfig } from "./types.js";
const DEFAULT_CONFIG: PluginConfig = {
cli: "dart",
publishPub: true,
updateBuildNumber: false,
};

const PUB_DEV_AUDIENCE = "https://pub.dev";
Expand Down
60 changes: 59 additions & 1 deletion tests/prepare.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ describe("prepare", () => {
const pubspecPath = "pubspec.yaml";
const cli = "dart";

const config: PluginConfig = { cli, publishPub: true };
const config: PluginConfig = {
cli,
publishPub: true,
updateBuildNumber: false,
};

const basePubspec = codeBlock`
name: pub_package
Expand Down Expand Up @@ -66,4 +70,58 @@ describe("prepare", () => {
expect(writeFileSync).toHaveBeenNthCalledWith(1, pubspecPath, newPubspec);
},
);

test("success with pubspec version with version code (updateBuildNumber = true)", async () => {
const newConfig = { ...config, updateBuildNumber: true };
const pubspec = basePubspec.replace(
new RegExp(versionPlaceholder),
`${newVersion}+1`,
);
vi.mocked(readFileSync).mockReturnValue(pubspec);

await prepare(newConfig, context);

const newPubspec = basePubspec.replace(
new RegExp(versionPlaceholder),
`${newVersion}+2`,
);

expect(readFileSync).toHaveBeenNthCalledWith(1, pubspecPath, "utf-8");
expect(writeFileSync).toHaveBeenNthCalledWith(1, pubspecPath, newPubspec);
});

test(`success with pubspec version without version code (updateBuildNumber = true)`, async () => {
const newConfig = { ...config, updateBuildNumber: true };
const pubspec = basePubspec.replace(
new RegExp(versionPlaceholder),
`${newVersion}`,
);
vi.mocked(readFileSync).mockReturnValue(pubspec);

await prepare(newConfig, context);

const newPubspec = basePubspec.replace(
new RegExp(versionPlaceholder),
`${newVersion}+1`,
);

expect(readFileSync).toHaveBeenNthCalledWith(1, pubspecPath, "utf-8");
expect(writeFileSync).toHaveBeenNthCalledWith(1, pubspecPath, newPubspec);
});

test("error due to invalid version code", async () => {
const newConfig = { ...config, updateBuildNumber: true };
const pubspec = basePubspec.replace(
new RegExp(versionPlaceholder),
`${newVersion}+invalid`,
);
vi.mocked(readFileSync).mockReturnValue(pubspec);

await expect(() => prepare(newConfig, context)).rejects.toThrowError(
/Invalid version code/,
);

expect(readFileSync).toHaveBeenNthCalledWith(1, pubspecPath, "utf-8");
expect(writeFileSync).toBeCalledTimes(0);
});
});
6 changes: 5 additions & 1 deletion tests/publish.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ describe("publish", () => {
const version = "1.2.3";
const semanticReleasePubToken = "SEMANTIC_RELEASE_PUB_TOKEN";

const config: PluginConfig = { cli, publishPub: true };
const config: PluginConfig = {
cli,
publishPub: true,
updateBuildNumber: false,
};
const nextRelease = mock<NextRelease>();
const logger = mock<Signale>();
const context = mock<PublishContext>();
Expand Down
1 change: 1 addition & 0 deletions tests/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ describe("getConfig", () => {
const config: PluginConfig = {
cli: "flutter",
publishPub: false,
updateBuildNumber: false,
};

test("success", () => {
Expand Down
6 changes: 5 additions & 1 deletion tests/verifyConditions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ describe("verifyConditions", () => {
const serviceAccount = "serviceAccount";
const idToken = "idToken";

const config: PluginConfig = { cli, publishPub: true };
const config: PluginConfig = {
cli,
publishPub: true,
updateBuildNumber: false,
};

const logger = mock<Signale>();
const context = mock<VerifyConditionsContext>();
Expand Down

0 comments on commit 80bf1f5

Please sign in to comment.