Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add sbt plugin #1962

Merged
merged 31 commits into from May 8, 2021
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
7fabc67
generate sbt plugin
laughedelic Apr 22, 2021
3572002
add code from git-tag, implement publish/canary hooks
laughedelic Apr 25, 2021
ca9252a
add publish logs to the canary details
laughedelic Apr 25, 2021
136aa7b
blank lines
laughedelic Apr 25, 2021
28fd6d6
add semver dependency
laughedelic Apr 25, 2021
437b4d3
refactor
laughedelic Apr 25, 2021
1f55712
add SNAPSHOT suffix to the canary version
laughedelic Apr 25, 2021
730b776
use strip-ansi
laughedelic Apr 25, 2021
db009e1
cut off v prefix
laughedelic Apr 25, 2021
5e71703
clean output log
laughedelic Apr 25, 2021
baa64ec
unused import
laughedelic Apr 25, 2021
c820bfd
add manageVersion config option
laughedelic Apr 25, 2021
7c6925e
docs
laughedelic Apr 25, 2021
84ca44d
linting
laughedelic Apr 25, 2021
5f91ad3
note about sbt version
laughedelic Apr 25, 2021
5f65920
remove unused test
laughedelic Apr 25, 2021
6b1ae33
don't aggregate version
laughedelic Apr 26, 2021
20bd60a
trim version output
laughedelic Apr 26, 2021
bef0fe9
downgrade strip-ansi
laughedelic Apr 26, 2021
d600aae
add publishCommand option
laughedelic Apr 27, 2021
8c2dda9
switch sbt-specific logs to verbose logger
laughedelic Apr 27, 2021
f6cfefc
adjust sbtClient interface
laughedelic Apr 27, 2021
6de4587
always set version on release
laughedelic Apr 27, 2021
552297e
rename manageVersion to setCanaryVersion
laughedelic Apr 27, 2021
266fdb6
pull sbt helpers out for testing
laughedelic Apr 27, 2021
ec195d3
first test for cleaning sbt client output
laughedelic Apr 27, 2021
788db29
tests for sbtGetVersion
laughedelic Apr 27, 2021
eba3fab
moar tests!
laughedelic Apr 27, 2021
47627ce
unused import
laughedelic Apr 27, 2021
bd18523
mention new plugin in the docs
laughedelic Apr 29, 2021
7ef81a8
add link to sbt's readme
laughedelic Apr 29, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
92 changes: 92 additions & 0 deletions plugins/sbt/README.md
@@ -0,0 +1,92 @@
# sbt plugin

Publish Scala projects with sbt

> :warning: only sbt 1.4+ is supported at the moment because this plugin uses `sbt --client` functionality

## Installation

This plugin is not included with the `auto` CLI installed via NPM. To install:

```bash
npm i --save-dev @auto-it/sbt
# or
yarn add -D @auto-it/sbt
```

## Usage

```json
{
"plugins": [
"sbt"
]
}
```

It is strongly recommended to use an sbt plugin to manage the version. There are a few options, but the most reliable and well maintained is [sbt-dynver](https://github.com/dwijnand/sbt-dynver). To enable it in your project add this line to `project/plugins.sbt`:

```scala
addSbtPlugin("com.dwijnand" % "sbt-dynver" % "x.y.z")
```

and then, depending on the publishing repository (e.g. if you are publishing to Sonatype Nexus), you might want to add

```scala
ThisBuild / dynverSeparator := "-"
ThisBuild / dynverSonatypeSnapshots := true
```

to your `build.sbt`.

With this setup canary versions will look like this: `{last_tag}-{number_of_commits}-{commit_sha}-SNAPSHOT`, for example:

```
0.1.2-5-fcdf268c-SNAPSHOT
```

## Options

### `setCanaryVersion: boolean` (default: `false`)

If you don't want to use an sbt plugin for version management, you can let Auto manage the canary version:

```json
{
"plugins": [
[
"sbt",
{
"setCanaryVersion": true
}
]
]
}
```

With this option Auto will override the version in sbt during canary release process.

Canary versions will look like this: `{last_tag}-canary.{pr_number}.{build_number}-SNAPSHOT`, for example:

```
0.1.2-canary.47.5fa1736-SNAPSHOT
```

Here build number is the git commit SHA.

### `publishCommand: string` (default: `publish`)

If you need to run some custom publishing command, you can change this option. For example, to cross-publish a library:

```json
{
"plugins": [
[
"sbt",
{
"publishCommand": "+publish"
}
]
]
}
```
213 changes: 213 additions & 0 deletions plugins/sbt/__tests__/sbt.test.ts
@@ -0,0 +1,213 @@
import * as Auto from "@auto-it/core";
import { dummyLog } from "@auto-it/core/dist/utils/logger";
import { makeHooks } from "@auto-it/core/dist/utils/make-hooks";
import SbtPlugin, { ISbtPluginOptions, sbtClient, sbtGetVersion } from "../src";

const exec = jest.fn();

jest.mock(
"../../../packages/core/dist/utils/exec-promise",
() => (...args: any[]) => exec(...args),
);

const rawOutput =
`[info] entering *experimental* thin client - BEEP WHIRR
[info] terminate the server with \`shutdown\`
> print version
1.2.3
[success] Total time: 2 s, completed Apr 27, 2021 3:39:23 AM
`;

const cleanedOutput = `1.2.3
[success] Total time: 2 s, completed Apr 27, 2021 3:39:23 AM`;

const rawAggregationOutput =
`[info] entering *experimental* thin client - BEEP WHIRR
[info] terminate the server with \`shutdown\`
> set version/aggregate := false
[info] Defining version / aggregate
[info] The new value will be used by no settings or tasks.
[info] Reapplying settings...
[info] set current project to auto-release-test-scala (in build file:/Users/user/project/)
[success] Total time: 2 s, completed Apr 27, 2021 3:52:04 AM
v`;

describe("sbt plugin", () => {
let hooks: Auto.IAutoHooks;
const prefixRelease: (a: string) => string = jest.fn(
(version) => `v${version}`,
);
const options: ISbtPluginOptions = {};
const logger = dummyLog();

const setup = (options: ISbtPluginOptions) => {
const plugin = new SbtPlugin(options);
hooks = makeHooks();
plugin.apply(
({
hooks,
logger,
remote: "stubRemote",
prefixRelease,
git: {
getLastTagNotInBaseBranch: async () => undefined,
getLatestRelease: async () => "0.0.1",
},
getCurrentVersion: async () => "0.0.1",
} as unknown) as Auto.Auto,
);
};

beforeEach(() => {
exec.mockClear();
setup(options);
});

describe("sbt client", () => {
test("should clean output", async () => {
exec.mockReturnValueOnce(rawOutput);
const output = await sbtClient("");
expect(output).toBe(cleanedOutput);
});

test("should parse version value", async () => {
exec
.mockReturnValueOnce(rawAggregationOutput)
.mockReturnValueOnce(rawOutput);
const output = await sbtGetVersion();
expect(output).toBe("1.2.3");
});

test("should error if it can't parse version value", async () => {
exec
.mockReturnValueOnce(rawAggregationOutput)
.mockReturnValueOnce("");
await expect(sbtGetVersion()).rejects.toThrowError(
`Failed to read version from sbt: `,
);
});
});

describe("version hook", () => {
test("should set version in sbt", async () => {
exec.mockReturnValue("");

await hooks.version.promise({
bump: Auto.SEMVER.minor,
});
expect(exec).toHaveBeenCalledTimes(2);
expect(exec).lastCalledWith("sbt", [
"--client",
'set every version := \\"0.1.0\\"',
]);
});
});

describe("publish hook", () => {
test("should call sbt publish", async () => {
exec.mockReturnValue("");

await hooks.publish.promise({
bump: Auto.SEMVER.minor,
});

expect(exec).toHaveBeenCalledWith("sbt", [
"--client",
"publish",
]);
});

test("should call sbt publish with custom command", async () => {
setup({
publishCommand: "+publishLocal",
});
exec.mockReturnValue("");

await hooks.publish.promise({
bump: Auto.SEMVER.minor,
});

expect(exec).toHaveBeenCalledWith("sbt", [
"--client",
"+publishLocal",
]);
});
});

describe("canary hook", () => {
test("should only read version from sbt on dry run", async () => {
exec
.mockReturnValueOnce(rawAggregationOutput)
.mockReturnValueOnce(rawOutput);

await hooks.canary.promise({
bump: Auto.SEMVER.minor,
canaryIdentifier: "-canary.42.1",
dryRun: true,
});

expect(exec).toHaveBeenCalledTimes(2); // 2 calls in sbtGetVersion
});

test("should return version from sbt as canary", async () => {
exec.mockReturnValue(rawOutput);

const result = await hooks.canary.promise({
bump: Auto.SEMVER.minor,
canaryIdentifier: "-canary.42.1",
});

expect(exec).not.toHaveBeenCalledWith("sbt", [
"--client",
'set every version := \\"0.1.0\\"',
]);

expect(result).toMatchObject({
newVersion: "1.2.3",
details: [
"```",
cleanedOutput,
"```",
].join("\n"),
});
});

test("should construct canary version when configured", async () => {
setup({
setCanaryVersion: true,
});
exec.mockReturnValue(rawOutput);

const result = await hooks.canary.promise({
bump: Auto.SEMVER.minor,
canaryIdentifier: "-canary.42.1",
});

const newVersion = "0.0.0-canary.42.1-SNAPSHOT";

expect(exec).toHaveBeenCalledWith("sbt", [
"--client",
`set every version := \\"${newVersion}\\"`,
]);

expect(result).toMatchObject({ newVersion });
});

test("should call sbt publish with custom command", async () => {
setup({
publishCommand: "+publishLocal",
});
exec.mockReturnValue(rawOutput);

await hooks.canary.promise({
bump: Auto.SEMVER.minor,
canaryIdentifier: "-canary.42.1",
});

expect(exec).toHaveBeenCalledWith("sbt", [
"--client",
"+publishLocal",
]);
});
});
});
48 changes: 48 additions & 0 deletions plugins/sbt/package.json
@@ -0,0 +1,48 @@
{
"name": "@auto-it/sbt",
"version": "10.25.1",
"main": "dist/index.js",
"description": "Publish Scala projects with sbt",
"license": "MIT",
"author": {
"name": "Alexey Alekhin",
"email": "laughedelic@gmail.com"
},
"publishConfig": {
"registry": "https://registry.npmjs.org/",
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/intuit/auto"
},
"files": [
"dist"
],
"keywords": [
"automation",
"semantic",
"release",
"github",
"labels",
"automated",
"continuos integration",
"changelog",
"scala",
"sbt"
],
"scripts": {
"build": "tsc -b",
"start": "npm run build -- -w",
"lint": "eslint src --ext .ts",
"test": "jest --maxWorkers=2 --config ../../package.json"
},
"dependencies": {
"@auto-it/core": "link:../../packages/core",
"fp-ts": "^2.5.3",
"io-ts": "^2.1.2",
"semver": "^7.0.0",
"strip-ansi": "^6.0.0",
"tslib": "1.10.0"
}
}