Skip to content

Commit

Permalink
Merge pull request #1510 from RichiCoder1/feature/add_docker_plugin
Browse files Browse the repository at this point in the history
Add Docker Publish Plugin
  • Loading branch information
hipstersmoothie committed Sep 8, 2020
2 parents 0819cd9 + b69600f commit 327241b
Show file tree
Hide file tree
Showing 9 changed files with 590 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Auto has an extensive plugin system and wide variety of official plugins. Make a
- [chrome](./plugins/chrome) - Publish code to Chrome Web Store
- [crates](./plugins/crates) - Publish Rust crates
- [cocoapods](./plugins/cocoapods) - Version your [Cocoapod](https://cocoapods.org/), and push to your specs repository!
- [docker](./plugins/docker) - Publish images with Docker
- [gem](./plugins/gem) - Publish ruby gems
- [git-tag](./plugins/git-tag) - Manage your projects version through just a git tag (`default` when used with binary)
- [gradle](./plugins/gradle) - Publish code with gradle
Expand Down Expand Up @@ -239,6 +240,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds

<!-- markdownlint-restore -->
<!-- prettier-ignore-end -->

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/kentcdodds/all-contributors) specification. Contributions of any kind welcome!
Expand Down
1 change: 1 addition & 0 deletions docs/pages/docs/_sidebar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Package Manager Plugins
- [Chrome Web Store](./generated/chrome)
- [Cocoapod](./generated/cocoapods)
- [Crates](./generated/crates)
- [Docker](./generated/docker)
- [Gem](./generated/gem)
- [Git Tag](./generated/git-tag)
- [Gradle](./generated/gradle)
Expand Down
Binary file modified packages/cli/auto
Binary file not shown.
50 changes: 50 additions & 0 deletions plugins/docker/README.md
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" }]]
}
```
281 changes: 281 additions & 0 deletions plugins/docker/__tests__/docker.test.ts
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`,
]);
});
});
});
Loading

0 comments on commit 327241b

Please sign in to comment.