-
Notifications
You must be signed in to change notification settings - Fork 202
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 #1510 from RichiCoder1/feature/add_docker_plugin
Add Docker Publish Plugin
- Loading branch information
Showing
9 changed files
with
590 additions
and
0 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
Binary file not shown.
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,50 @@ | ||
# Docker Plugin | ||
|
||
This plugin automates tagging and publishing images to a docker registry. | ||
|
||
## Prerequisites | ||
|
||
To publish to a docker registry, you'll first need to authenticate with the target registry. For example, the [Docker Login Action](https://github.com/docker/login-action) for GitHub, or [the `withRegistry` helper](https://www.jenkins.io/doc/book/pipeline/docker/#custom-registry) in Jenkins. | ||
|
||
## Installation | ||
|
||
This plugin is not included with the `auto` CLI installed via NPM. To install: | ||
|
||
```bash | ||
npm i --save-dev @auto-it/docker | ||
# or | ||
yarn add -D @auto-it/docker | ||
``` | ||
|
||
## Usage | ||
|
||
You must first must build the desired image to publish. | ||
|
||
These environment variables tell `auto` what to publish. | ||
|
||
- IMAGE - The image ID, digest, or tag of the locally available image to tag and publish. This is required unless you want to statically tag the local image, in which case you can provide it as an option. | ||
|
||
```json | ||
{ | ||
"plugins": [ | ||
["docker", { "registry": "ghcr.io/my/app" }] | ||
// other plugins | ||
] | ||
} | ||
``` | ||
|
||
If you'd like to tag releases with `latest` too, you can specify the `tagLatest` option: | ||
|
||
```json | ||
{ | ||
"plugins": [["docker", { "registry": "ghcr.io/my/app", "tagLatest": true }]] | ||
} | ||
``` | ||
|
||
If you're tagging the locally built image in a static manner, you can also pass `image` instead of `IMAGE` as an environment variable. | ||
|
||
```json | ||
{ | ||
"plugins": [["docker", { "registry": "ghcr.io/my/app", "image": "myapp" }]] | ||
} | ||
``` |
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,281 @@ | ||
import * as Auto from "@auto-it/core"; | ||
import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; | ||
import { dummyLog } from "@auto-it/core/dist/utils/logger"; | ||
|
||
import DockerPlugin, { IDockerPluginOptions } from "../src"; | ||
|
||
const exec = jest.fn(); | ||
jest.mock("../../../packages/core/dist/utils/get-current-branch", () => ({ | ||
getCurrentBranch: () => "next", | ||
})); | ||
jest.mock("../../../packages/core/dist/utils/exec-promise", () => ({ | ||
// @ts-ignore | ||
default: (...args) => exec(...args), | ||
})); | ||
|
||
const registry = "registry.io/app"; | ||
|
||
const setup = ( | ||
mockGit?: any, | ||
options?: IDockerPluginOptions, | ||
checkEnv?: jest.SpyInstance | ||
) => { | ||
const plugin = new DockerPlugin(options || { registry }); | ||
const hooks = makeHooks(); | ||
|
||
plugin.apply(({ | ||
checkEnv, | ||
hooks, | ||
git: mockGit, | ||
remote: "origin", | ||
logger: dummyLog(), | ||
prefixRelease: (r: string) => r, | ||
config: { prereleaseBranches: ["next"] }, | ||
getCurrentVersion: () => "v1.0.0", | ||
} as unknown) as Auto.Auto); | ||
|
||
return hooks; | ||
}; | ||
|
||
describe("Docker Plugin", () => { | ||
beforeEach(() => { | ||
exec.mockClear(); | ||
}); | ||
|
||
describe("validateConfig", () => { | ||
test("should error without options", async () => { | ||
const hooks = setup(); | ||
await expect( | ||
hooks.validateConfig.promise("docker", null) | ||
).resolves.toHaveLength(1); | ||
}); | ||
|
||
test("should pass with valid options", async () => { | ||
const hooks = setup(); | ||
await expect( | ||
hooks.validateConfig.promise("docker", { registry: registry }) | ||
).resolves.toHaveLength(0); | ||
}); | ||
}); | ||
|
||
describe("beforeRun", () => { | ||
test("should check env without image", async () => { | ||
const checkEnv = jest.fn(); | ||
const hooks = setup(undefined, undefined, checkEnv); | ||
await hooks.beforeRun.promise({ | ||
plugins: [["docker", { registry }]], | ||
} as any); | ||
expect(checkEnv).toBeCalled(); | ||
}); | ||
|
||
test("shouldn't check env with image", async () => { | ||
const checkEnv = jest.fn(); | ||
const hooks = setup(undefined, undefined, checkEnv); | ||
await hooks.beforeRun.promise({ | ||
plugins: [["docker", { registry, image: "test" }]], | ||
} as any); | ||
expect(checkEnv).not.toBeCalled(); | ||
}); | ||
}); | ||
|
||
describe("getPreviousVersion", () => { | ||
test("should error without git", async () => { | ||
const hooks = setup(); | ||
await expect(hooks.getPreviousVersion.promise()).rejects.toBeInstanceOf( | ||
Error | ||
); | ||
}); | ||
|
||
test("should get previous version", async () => { | ||
const hooks = setup({ getLatestTagInBranch: () => "v1.0.0" }); | ||
const previousVersion = await hooks.getPreviousVersion.promise(); | ||
expect(previousVersion).toBe("v1.0.0"); | ||
}); | ||
|
||
test("should default to 0.0.0 when no previous version", async () => { | ||
const hooks = setup({ | ||
getLatestTagInBranch: () => { | ||
throw new Error(); | ||
}, | ||
}); | ||
const previousVersion = await hooks.getPreviousVersion.promise(); | ||
expect(previousVersion).toBe("0.0.0"); | ||
}); | ||
}); | ||
|
||
describe("version", () => { | ||
test("should do nothing without git", async () => { | ||
const hooks = setup(); | ||
await hooks.version.promise(Auto.SEMVER.patch); | ||
expect(exec).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test("should do nothing with a bad version bump", async () => { | ||
const hooks = setup({ getLatestTagInBranch: () => "v1.0.0" }); | ||
await hooks.version.promise("wrong" as Auto.SEMVER); | ||
expect(exec).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test("should tag next version", async () => { | ||
const sourceImage = "app:sha-123"; | ||
const hooks = setup( | ||
{ getLatestTagInBranch: () => "v1.0.0" }, | ||
{ registry, image: sourceImage } | ||
); | ||
await hooks.version.promise(Auto.SEMVER.patch); | ||
expect(exec).toHaveBeenCalledWith("git", [ | ||
"tag", | ||
"1.0.1", | ||
"-m", | ||
'"Update version to 1.0.1"', | ||
]); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"tag", | ||
sourceImage, | ||
`${registry}:1.0.1`, | ||
]); | ||
}); | ||
|
||
test("should tag latest", async () => { | ||
const sourceImage = "app:sha-123"; | ||
const hooks = setup( | ||
{ getLatestTagInBranch: () => "v1.0.0" }, | ||
{ registry, image: sourceImage, tagLatest: true } | ||
); | ||
await hooks.version.promise(Auto.SEMVER.patch); | ||
expect(exec).toHaveBeenCalledWith("git", [ | ||
"tag", | ||
"1.0.1", | ||
"-m", | ||
'"Update version to 1.0.1"', | ||
]); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"tag", | ||
sourceImage, | ||
`${registry}:1.0.1`, | ||
]); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"tag", | ||
sourceImage, | ||
`${registry}:latest`, | ||
]); | ||
}); | ||
}); | ||
|
||
describe("canary", () => { | ||
test("should do nothing without git", async () => { | ||
const hooks = setup(); | ||
await hooks.next.promise([], Auto.SEMVER.patch, {} as any); | ||
expect(exec).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test("should tag canary version", async () => { | ||
const sourceImage = "app:sha-123"; | ||
const hooks = setup( | ||
{ | ||
getLatestRelease: () => "v1.0.0", | ||
getCurrentVersion: () => "v1.0.0", | ||
}, | ||
{ registry, image: sourceImage } | ||
); | ||
|
||
await hooks.canary.promise(Auto.SEMVER.patch, ".123.1"); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"tag", | ||
sourceImage, | ||
`${registry}:1.0.1-canary.123.1`, | ||
]); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"push", | ||
`${registry}:1.0.1-canary.123.1`, | ||
]); | ||
}); | ||
}); | ||
|
||
describe("next", () => { | ||
test("should do nothing without git", async () => { | ||
const hooks = setup(); | ||
await hooks.next.promise([], Auto.SEMVER.patch, {} as any); | ||
expect(exec).not.toHaveBeenCalled(); | ||
}); | ||
|
||
test("should tag next version", async () => { | ||
const sourceImage = "app:sha-123"; | ||
const hooks = setup( | ||
{ | ||
getLatestRelease: () => "v0.1.0", | ||
getLastTagNotInBaseBranch: () => "v1.0.0", | ||
}, | ||
{ registry, image: sourceImage } | ||
); | ||
|
||
await hooks.next.promise([], Auto.SEMVER.patch, {} as any); | ||
|
||
expect(exec).toHaveBeenCalledWith("git", [ | ||
"tag", | ||
"1.0.1-next.0", | ||
"-m", | ||
'"Tag pre-release: 1.0.1-next.0"', | ||
]); | ||
expect(exec).toHaveBeenCalledWith("git", [ | ||
"push", | ||
"origin", | ||
"next", | ||
"--tags", | ||
]); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"tag", | ||
sourceImage, | ||
`${registry}:1.0.1-next.0`, | ||
]); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"push", | ||
`${registry}:1.0.1-next.0`, | ||
]); | ||
}); | ||
}); | ||
|
||
describe("publish", () => { | ||
test("should publish next version", async () => { | ||
const sourceImage = "app:sha-123"; | ||
const hooks = setup( | ||
{ | ||
getLatestTagInBranch: () => "v1.0.0", | ||
getCurrentBranch: () => "master", | ||
remote: "github.com", | ||
}, | ||
{ registry, image: sourceImage, tagLatest: false } | ||
); | ||
await hooks.version.promise(Auto.SEMVER.patch); | ||
|
||
await hooks.publish.promise(Auto.SEMVER.patch); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"push", | ||
`${registry}:1.0.1`, | ||
]); | ||
}); | ||
|
||
test("should publish latest", async () => { | ||
const sourceImage = "app:sha-123"; | ||
const hooks = setup( | ||
{ | ||
getLatestTagInBranch: () => "v1.0.0", | ||
getCurrentBranch: () => "master", | ||
remote: "github.com", | ||
}, | ||
{ registry, image: sourceImage, tagLatest: true } | ||
); | ||
await hooks.version.promise(Auto.SEMVER.patch); | ||
|
||
await hooks.publish.promise(Auto.SEMVER.patch); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"push", | ||
`${registry}:1.0.1`, | ||
]); | ||
expect(exec).toHaveBeenCalledWith("docker", [ | ||
"push", | ||
`${registry}:latest`, | ||
]); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.