-
Notifications
You must be signed in to change notification settings - Fork 205
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1962 from laughedelic/feat/plugin/sbt
add sbt plugin
- Loading branch information
Showing
9 changed files
with
579 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
# sbt plugin | ||
|
||
Publish Scala projects with [sbt](https://www.scala-sbt.org) | ||
|
||
> :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" | ||
} | ||
] | ||
] | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = | ||
`[[0minfo[0m] entering *experimental* thin client - BEEP WHIRR | ||
[[0minfo[0m] terminate the server with \`shutdown\` | ||
> print version | ||
1.2.3[0J | ||
[0J[[32msuccess[0m] Total time: 2 s, completed Apr 27, 2021 3:39:23 AM | ||
[0J`; | ||
|
||
const cleanedOutput = `1.2.3 | ||
[success] Total time: 2 s, completed Apr 27, 2021 3:39:23 AM`; | ||
|
||
const rawAggregationOutput = | ||
`[[0minfo[0m] entering *experimental* thin client - BEEP WHIRR | ||
[[0minfo[0m] terminate the server with \`shutdown\` | ||
> set version/aggregate := false | ||
[info] Defining version / aggregate[0J | ||
[0J[info] The new value will be used by no settings or tasks.[0J | ||
[0J[info] Reapplying settings...[0J | ||
[0J[info] set current project to auto-release-test-scala (in build file:/Users/user/project/)[0J | ||
[0J[[32msuccess[0m] Total time: 2 s, completed Apr 27, 2021 3:52:04 AM | ||
[0Jv`; | ||
|
||
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", | ||
]); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
} | ||
} |
Oops, something went wrong.