From 7a7942192c3d421655770865bbf438343f81d028 Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Sat, 30 Apr 2022 01:15:23 +0530 Subject: [PATCH 01/12] Adding auto Fastlane plugin --- plugins/fastlane/README.md | 47 + plugins/fastlane/__tests__/cocoapods.test.ts | 1038 ++++++++++++++++++ plugins/fastlane/package.json | 45 + plugins/fastlane/src/index.ts | 405 +++++++ plugins/fastlane/src/utilities.ts | 22 + plugins/fastlane/tsconfig.json | 16 + 6 files changed, 1573 insertions(+) create mode 100644 plugins/fastlane/README.md create mode 100644 plugins/fastlane/__tests__/cocoapods.test.ts create mode 100644 plugins/fastlane/package.json create mode 100644 plugins/fastlane/src/index.ts create mode 100644 plugins/fastlane/src/utilities.ts create mode 100644 plugins/fastlane/tsconfig.json diff --git a/plugins/fastlane/README.md b/plugins/fastlane/README.md new file mode 100644 index 000000000..ec3f43d0a --- /dev/null +++ b/plugins/fastlane/README.md @@ -0,0 +1,47 @@ +# Fastlane Plugin + +Use `auto` to version your [pList] using fastlane, and push to your repository! + +## Installation + +This plugin is not included with the `auto` CLI installed via NPM. To install: + +```bash +npm i --save-dev @auto-it/fastlane +# or +yarn add -D @auto-it/fastlane +``` + +> WARNING: You can only use one "package manager" at a time! +> Mixing them will lead to undesired results. + +## Usage + +```json +{ + "plugins": [ + [ + "fastlane", + { + // Required, the relative path to your pList file + "pListPath": "./Example/Info.plist", + // Optional, specify a different executable for `fastlane` + "fastlaneCommand": "bundle exec fastlane" + } + ] + // other plugins + ] +} +``` + +## Requirements + +### General + +- The machine running this plugin must have the [fastlane](https://fastlane.tools/) `fastlane` CLI installed already, or `fastlaneCommand` specified in your plugin configuration. +- Using the logging flags with Auto (`auto -v`, `auto -vv`, `auto -q`) will also add the verbose or silent flags to the fastlane commands. + +### Pushing to the Repository + + +### Pushing to a private repository \ No newline at end of file diff --git a/plugins/fastlane/__tests__/cocoapods.test.ts b/plugins/fastlane/__tests__/cocoapods.test.ts new file mode 100644 index 000000000..bd25574b6 --- /dev/null +++ b/plugins/fastlane/__tests__/cocoapods.test.ts @@ -0,0 +1,1038 @@ +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 * as utilities from "../src/utilities"; +import CocoapodsPlugin, { + ICocoapodsPluginOptions, + getParsedPodspecContents, + getVersion, + updatePodspecVersion, + updateSourceLocation, + getSourceInfo, +} from "../src"; + +const specWithVersion = ( + version: string, + source = "{ :git => 'https://github.com/intuit/auto.git', :tag => s.version.to_s }" +) => ` + Pod:: Spec.new do | s | + s.name = 'Test' + s.version = '${version}' + s.summary = 'A short description of Test.' + + # This description is used to generate tags and improve search results. + # * Think: What does it do? Why did you write it ? What is the focus ? + # * Try to keep it short, snappy and to the point. + # * Write the description between the DESC delimiters below. + # * Finally, don't worry about the indent, CocoaPods strips it! + + s.description = << -DESC + TODO: Add long description of the pod here. + DESC + + s.homepage = 'https://github.com/intuit/auto' + # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' + s.license = { : type => 'MIT', : file => 'LICENSE' } + s.author = { 'hborawski' => 'harris_borawski@intuit.com' } + s.source = ${source} + + s.ios.deployment_target = '11.0' + + s.source_files = 'Test/Classes/**/*' + + + # s.public_header_files = 'Pod/Classes/**/*.h' + # s.frameworks = 'UIKit', 'MapKit' + # s.dependency 'Alamofire' + end + `; + +const mockPodspec = (contents: string) => { + return jest.spyOn(utilities, "getPodspecContents").mockReturnValue(contents); +}; + +interface FakePodspec { + path: string; + contents: string; +} +const mockPodspecs = (contents: FakePodspec[]) => { + return jest + .spyOn(utilities, "getPodspecContents") + .mockImplementation((path) => { + return contents.find((podspec) => podspec.path === path).contents; + }); +}; + +let exec = jest.fn(); +// @ts-ignore +jest.mock("../../../packages/core/dist/utils/exec-promise", () => (...args) => + exec(...args) +); +const logger = dummyLog(); + +describe("Cocoapods Plugin", () => { + let hooks: Auto.IAutoHooks; + let multiHooks: Auto.IAutoHooks; + const prefixRelease: (a: string) => string = (version: string) => { + return `v${version}`; + }; + + const options: ICocoapodsPluginOptions = { + podspecPath: "./Test.podspec", + }; + + const multiOptions: ICocoapodsPluginOptions = { + podspecPath: ["./Test.podspec", "./Test2.podspec"], + }; + + beforeEach(() => { + jest.resetAllMocks(); + exec.mockClear(); + const plugin = new CocoapodsPlugin(options); + const multiPlugin = new CocoapodsPlugin(multiOptions); + hooks = makeHooks(); + multiHooks = makeHooks(); + const apply = (p: CocoapodsPlugin, h: Auto.IAutoHooks) => { + p.apply(({ + hooks: h, + logger: logger, + prefixRelease, + git: { + getLastTagNotInBaseBranch: async () => undefined, + getLatestRelease: async () => "0.0.1", + getPullRequest: async () => ({ + data: { + head: { + repo: { + clone_url: "https://github.com/intuit-fork/auto.git", + }, + }, + }, + }), + }, + remote: "https://github.com/intuit/auto.git", + getCurrentVersion: async () => "0.0.1", + } as unknown) as Auto.Auto); + }; + + apply(plugin, hooks); + apply(multiPlugin, multiHooks); + }); + + describe("getParsedPodspecContents", () => { + test("should return null if contents cant be parsed with regex", () => { + mockPodspec("bad podspec"); + + expect(getParsedPodspecContents("./Test.podspec")).toBeNull(); + }); + test("should return parsed contents", () => { + mockPodspec(specWithVersion("0.0.1")); + const contents = getParsedPodspecContents("./Test.podspec"); + expect(contents).toHaveProperty("groups", { version: "0.0.1" }); + }); + }); + describe("getVersion", () => { + test("should throw error if parsed podspec is returned as null", () => { + mockPodspec("bad podspec"); + + expect(() => getVersion("./Test.podspec")).toThrow(); + }); + test("should return version", () => { + mockPodspec(specWithVersion("0.0.1")); + + expect(getVersion("./Test.podspec")).toBe("0.0.1"); + }); + test("should return canary version", () => { + mockPodspec(specWithVersion("0.0.1-canary.1.0.0")); + + expect(getVersion("./Test.podspec")).toBe("0.0.1-canary.1.0.0"); + }); + }); + describe("getSourceInfo", () => { + test("should throw error if source line cant be found", () => { + mockPodspec(specWithVersion("0.0.1", "no source")); + + expect(() => getSourceInfo("./Test.podspec")).toThrow(); + }); + test("should retrieve source info", () => { + mockPodspec(specWithVersion("0.0.1")); + + expect(getSourceInfo("./Test.podspec")).toBe( + "{ :git => 'https://github.com/intuit/auto.git', :tag => s.version.to_s }" + ); + }); + }); + describe("updatePodspecVersion", () => { + test("should throw error if there is an error writing file", async () => { + mockPodspec(specWithVersion("0.0.1")); + + jest + .spyOn(utilities, "writePodspecContents") + .mockImplementationOnce(() => { + throw new Error("Filesystem Error"); + }); + + expect( + updatePodspecVersion.bind(null, "./Test.podspec", "0.0.2") + ).toThrowError("Error updating version in podspec: ./Test.podspec"); + }); + test("should successfully write new version", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const mock = jest.spyOn(utilities, "writePodspecContents"); + + await updatePodspecVersion("./Test.podspec", "0.0.2"); + expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); + }); + }); + describe("updateSourceLocation", () => { + test("should throw error if there is an error writing file", async () => { + mockPodspec(specWithVersion("0.0.1")); + + exec.mockReturnValue("commithash"); + + jest + .spyOn(utilities, "writePodspecContents") + .mockImplementationOnce(() => { + throw new Error("Filesystem Error"); + }); + + await expect( + updateSourceLocation( + "./Test.podspec", + "https://github.com/somefork/auto.git" + ) + ).rejects.toThrowError( + "Error updating source location in podspec: ./Test.podspec" + ); + }); + test("should successfully write new source location", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const mock = jest.spyOn(utilities, "writePodspecContents"); + + exec.mockReturnValue(Promise.resolve("commithash")); + + await updateSourceLocation( + "./Test.podspec", + "https://github.com/somefork/auto.git" + ); + expect(mock).lastCalledWith( + expect.any(String), + specWithVersion( + "0.0.1", + "{ :git => 'https://github.com/somefork/auto.git', :commit => 'commithash' }" + ) + ); + }); + }); + describe("validateConfig hook", () => { + test("should validate options", async () => { + expect( + ((await hooks.validateConfig.promise("cocoapods", {})) || []) + ).toHaveLength(1); + expect( + ((await hooks.validateConfig.promise("cocoapods", options)) || []) + + ).toHaveLength(0); + expect( + ((await hooks.validateConfig.promise("cocoapods", multiOptions)) || []) + + ).toHaveLength(0); + }); + }); + describe("modifyConfig hook", () => { + test("should set noVersionPrefix to true", async () => { + const config = {}; + expect(await hooks.modifyConfig.promise(config as any)).toStrictEqual({ + noVersionPrefix: true, + }); + }); + }); + describe("getPreviousVersion hook", () => { + test("should get previous version from podspec", async () => { + mockPodspec(specWithVersion("0.0.1")); + + expect(await hooks.getPreviousVersion.promise()).toBe("v0.0.1"); + }); + + test("should throw if no version found", async () => { + mockPodspec(specWithVersion("")); + + await expect(hooks.getPreviousVersion.promise()).rejects.toThrowError( + "Version could not be found in podspec: ./Test.podspec" + ); + }); + + test("should get version if multiple podspecs", async () => { + mockPodspecs([ + { + path: "./Test.podspec", + contents: specWithVersion("0.0.1"), + }, + { + path: "./Test2.podspec", + contents: specWithVersion("0.0.1"), + }, + ]); + expect(await multiHooks.getPreviousVersion.promise()).toBe("v0.0.1"); + }); + }); + describe("version hook", () => { + test("should do nothing on dryRun", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const mockLog = jest.spyOn(logger.log, "info"); + + await hooks.version.promise({ bump: Auto.SEMVER.patch, dryRun: true }); + + expect(exec).toHaveBeenCalledTimes(0); + expect(mockLog).toHaveBeenCalledTimes(1); + }); + test("should not use logger on quiet dryRun", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const mockLog = jest.spyOn(logger.log, "info"); + const mockConsole = jest.spyOn(console, "log"); + + await hooks.version.promise({ + bump: Auto.SEMVER.patch, + dryRun: true, + quiet: true, + }); + + expect(exec).toHaveBeenCalledTimes(0); + expect(mockLog).toHaveBeenCalledTimes(0); + expect(mockConsole).toHaveBeenCalledTimes(1); + }); + test("should version release - patch version", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const mock = jest.spyOn(utilities, "writePodspecContents"); + + await hooks.version.promise({ bump: Auto.SEMVER.patch }); + + expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); + }); + test("should version release - minor version", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const mock = jest.spyOn(utilities, "writePodspecContents"); + + await hooks.version.promise({ bump: Auto.SEMVER.minor }); + + expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.1.0")); + }); + test("should version release - major version", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const mock = jest.spyOn(utilities, "writePodspecContents"); + + await hooks.version.promise({ bump: Auto.SEMVER.major }); + + expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); + }); + test("should release version multiple podspecs - patch", async () => { + mockPodspecs([ + { + path: "./Test.podspec", + contents: specWithVersion("0.0.1"), + }, + { + path: "./Test2.podspec", + contents: specWithVersion("0.0.1"), + }, + ]); + const mock = jest.spyOn(utilities, "writePodspecContents"); + await multiHooks.version.promise({ bump: Auto.SEMVER.patch }); + expect(mock).toHaveBeenCalledTimes(2); + expect(mock).toHaveBeenNthCalledWith( + 1, + "./Test.podspec", + specWithVersion("0.0.2") + ); + expect(mock).toHaveBeenNthCalledWith( + 2, + "./Test2.podspec", + specWithVersion("0.0.2") + ); + }); + test("should release version multiple podspecs - minor", async () => { + mockPodspecs([ + { + path: "./Test.podspec", + contents: specWithVersion("0.0.1"), + }, + { + path: "./Test2.podspec", + contents: specWithVersion("0.0.1"), + }, + ]); + const mock = jest.spyOn(utilities, "writePodspecContents"); + await multiHooks.version.promise({ bump: Auto.SEMVER.minor }); + expect(mock).toHaveBeenCalledTimes(2); + expect(mock).toHaveBeenNthCalledWith( + 1, + "./Test.podspec", + specWithVersion("0.1.0") + ); + expect(mock).toHaveBeenNthCalledWith( + 2, + "./Test2.podspec", + specWithVersion("0.1.0") + ); + }); + test("should release version multiple podspecs - major", async () => { + mockPodspecs([ + { + path: "./Test.podspec", + contents: specWithVersion("0.0.1"), + }, + { + path: "./Test2.podspec", + contents: specWithVersion("0.0.1"), + }, + ]); + const mock = jest.spyOn(utilities, "writePodspecContents"); + await multiHooks.version.promise({ bump: Auto.SEMVER.major }); + expect(mock).toHaveBeenCalledTimes(2); + expect(mock).toHaveBeenNthCalledWith( + 1, + "./Test.podspec", + specWithVersion("1.0.0") + ); + expect(mock).toHaveBeenNthCalledWith( + 2, + "./Test2.podspec", + specWithVersion("1.0.0") + ); + }); + test("should throw if there is an error writing new version", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const mock = jest + .spyOn(utilities, "writePodspecContents") + .mockImplementation((path, contents) => { + if (contents.includes("1.0.0")) { + throw new Error("Filesystem Error"); + } + }); + + await expect( + hooks.version.promise({ bump: Auto.SEMVER.major }) + ).rejects.toThrowError( + "Error updating version in podspec: ./Test.podspec" + ); + + expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); + }); + }); + + describe("beforeShipit hook", () => { + test("should call pod lib lint with dryRun flag", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const plugin = new CocoapodsPlugin(options); + const hook = makeHooks(); + + plugin.apply({ + hooks: hook, + logger: dummyLog(), + prefixRelease, + } as Auto.Auto); + + await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); + + expect(exec).toBeCalledTimes(1); + expect(exec).lastCalledWith("pod", ["lib", "lint", "./Test.podspec"]); + }); + test("should call pod lib lint for each podspec with dryRun flag", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const plugin = new CocoapodsPlugin(multiOptions); + const hook = makeHooks(); + + plugin.apply({ + hooks: hook, + logger: dummyLog(), + prefixRelease, + } as Auto.Auto); + + await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); + + expect(exec).toBeCalledTimes(2); + expect(exec).toHaveBeenNthCalledWith(1, "pod", [ + "lib", + "lint", + "./Test.podspec", + ]); + expect(exec).toHaveBeenNthCalledWith(2, "pod", [ + "lib", + "lint", + "./Test2.podspec", + ]); + }); + test("should call pod lib lint with options with dryRun flag", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const plugin = new CocoapodsPlugin({ + ...options, + flags: ["--flag"], + podCommand: "notpod", + }); + const hook = makeHooks(); + + plugin.apply({ + hooks: hook, + logger: dummyLog(), + prefixRelease, + } as Auto.Auto); + + await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); + + expect(exec).toBeCalledTimes(1); + expect(exec).lastCalledWith("notpod", [ + "lib", + "lint", + "--flag", + "./Test.podspec", + ]); + }); + }); + + describe("canary hook", () => { + test("should do nothing on dryRun", async () => { + mockPodspec(specWithVersion("0.0.1")); + jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); + + const mockLog = jest.spyOn(logger.log, "info"); + + await hooks.canary.promise({ + bump: Auto.SEMVER.patch, + canaryIdentifier: "canary.1.0", + dryRun: true, + }); + + expect(exec).toHaveBeenCalledTimes(0); + expect(mockLog).toHaveBeenCalledTimes(1); + }); + test("should not use logger on quiet dryRun", async () => { + mockPodspec(specWithVersion("0.0.1")); + jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); + + const mockLog = jest.spyOn(logger.log, "info"); + const mockConsole = jest.spyOn(console, "log"); + + await hooks.canary.promise({ + bump: Auto.SEMVER.patch, + canaryIdentifier: "canary.1.0", + dryRun: true, + quiet: true, + }); + + expect(exec).toHaveBeenCalledTimes(0); + expect(mockLog).toHaveBeenCalledTimes(0); + expect(mockConsole).toHaveBeenCalledTimes(1); + }); + test("should tag with canary version", async () => { + jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); + let podSpec = specWithVersion("0.0.1"); + jest + .spyOn(utilities, "getPodspecContents") + .mockImplementation(() => podSpec); + const mock = jest + .spyOn(utilities, "writePodspecContents") + .mockImplementation((path, contents) => { + podSpec = contents; + }); + + const newVersion = await hooks.canary.promise({ + bump: "minor" as Auto.SEMVER, + canaryIdentifier: "canary.1.1.1", + }); + + expect(newVersion).toBe("0.1.0-canary.1.1.1"); + expect(exec).toBeCalledTimes(3); + expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); + + expect(mock).toHaveBeenLastCalledWith( + expect.any(String), + specWithVersion( + "0.1.0-canary.1.1.1", + "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" + ) + ); + }); + test("should tag multiple podspeccs with canary version", async () => { + jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); + const specs = { + "./Test.podspec": specWithVersion("0.0.1"), + "./Test2.podspec": specWithVersion("0.0.1"), + }; + jest + .spyOn(utilities, "getPodspecContents") + .mockImplementation((path) => specs[path]); + const mock = jest + .spyOn(utilities, "writePodspecContents") + .mockImplementation((path, contents) => { + specs[path] = contents; + }); + + const newVersion = await multiHooks.canary.promise({ + bump: "minor" as Auto.SEMVER, + canaryIdentifier: "canary.1.1.1", + }); + + expect(newVersion).toBe("0.1.0-canary.1.1.1"); + expect(exec).toBeCalledTimes(6); + expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); + expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test2.podspec"]); + expect(mock).toBeCalledTimes(4); + expect(mock).toHaveBeenCalledWith( + "./Test.podspec", + specWithVersion( + "0.1.0-canary.1.1.1", + "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" + ) + ); + expect(mock).toHaveBeenCalledWith( + "./Test2.podspec", + specWithVersion( + "0.1.0-canary.1.1.1", + "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" + ) + ); + }); + test("should tag with canary version with no PR number", async () => { + let podSpec = specWithVersion("0.0.1"); + jest + .spyOn(utilities, "getPodspecContents") + .mockImplementation(() => podSpec); + const mock = jest + .spyOn(utilities, "writePodspecContents") + .mockImplementation((path, contents) => { + podSpec = contents; + }); + + const newVersion = await hooks.canary.promise({ + bump: "minor" as Auto.SEMVER, + canaryIdentifier: "canary.1.1.1", + }); + + expect(newVersion).toBe("0.1.0-canary.1.1.1"); + expect(exec).toBeCalledTimes(3); + expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); + + expect(mock).toHaveBeenLastCalledWith( + expect.any(String), + specWithVersion( + "0.1.0-canary.1.1.1", + "{ :git => 'https://github.com/intuit/auto.git', :commit => 'undefined' }" + ) + ); + }); + }); + + describe("next hook", () => { + test("should return prerelease versions on dryrun", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const versions = await hooks.next.promise(["0.0.1", "0.0.2"], { + bump: Auto.SEMVER.minor, + dryRun: true, + commits: [], + fullReleaseNotes: "", + releaseNotes: "", + }); + expect(versions).toStrictEqual(["0.0.1", "0.0.2", "0.1.0-next.0"]); + }); + test("should tag with next version", async () => { + jest.spyOn(Auto, "getCurrentBranch").mockReturnValue("next"); + const specs = { + "./Test.podspec": specWithVersion("0.0.1"), + "./Test2.podspec": specWithVersion("0.0.1"), + }; + jest + .spyOn(utilities, "getPodspecContents") + .mockImplementation((path) => specs[path]); + const mock = jest + .spyOn(utilities, "writePodspecContents") + .mockImplementation((path, contents) => { + specs[path] = contents; + }); + + const versions = await multiHooks.next.promise([], { + bump: Auto.SEMVER.major, + dryRun: false, + commits: [], + fullReleaseNotes: "", + releaseNotes: "", + }); + + expect(versions).toContain("1.0.0-next.0"); + expect(exec).toBeCalledTimes(6); + expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); + + expect(mock).toBeCalledTimes(2); + expect(mock).toHaveBeenCalledWith( + "./Test.podspec", + specWithVersion("1.0.0-next.0") + ); + expect(mock).toHaveBeenCalledWith( + "./Test2.podspec", + specWithVersion("1.0.0-next.0") + ); + }); + test("should tag with next version for multiple podspecs", async () => { + jest.spyOn(Auto, "getCurrentBranch").mockReturnValue("next"); + let podSpec = specWithVersion("0.0.1"); + jest + .spyOn(utilities, "getPodspecContents") + .mockImplementation(() => podSpec); + const mock = jest + .spyOn(utilities, "writePodspecContents") + .mockImplementation((path, contents) => { + podSpec = contents; + }); + + const versions = await hooks.next.promise([], { + bump: Auto.SEMVER.major, + dryRun: false, + commits: [], + fullReleaseNotes: "", + releaseNotes: "", + }); + + expect(versions).toContain("1.0.0-next.0"); + expect(exec).toBeCalledTimes(4); + expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); + + expect(mock).toHaveBeenLastCalledWith( + expect.any(String), + specWithVersion("1.0.0-next.0") + ); + }); + }); + + describe("publish hook", () => { + test("should push to trunk if no specsRepo in options", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const plugin = new CocoapodsPlugin(options); + const hook = makeHooks(); + plugin.apply({ + hooks: hook, + logger: dummyLog(), + prefixRelease, + } as Auto.Auto); + + await hook.publish.promise({ bump: Auto.SEMVER.patch }); + + expect(exec).toBeCalledTimes(2); + expect(exec).lastCalledWith("pod", ["trunk", "push", "./Test.podspec"]); + }); + test("should push multiple podspecs to trunk if no specsRepo in options", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const plugin = new CocoapodsPlugin(multiOptions); + const hook = makeHooks(); + plugin.apply({ + hooks: hook, + logger: dummyLog(), + prefixRelease, + } as Auto.Auto); + + await hook.publish.promise({ bump: Auto.SEMVER.patch }); + + expect(exec).toBeCalledTimes(3); + expect(exec).toHaveBeenCalledWith("pod", [ + "trunk", + "push", + "./Test.podspec", + ]); + expect(exec).toHaveBeenCalledWith("pod", [ + "trunk", + "push", + "./Test2.podspec", + ]); + }); + + test("should push with different pod command if in options", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const plugin = new CocoapodsPlugin({ ...options, podCommand: "notpod" }); + const hook = makeHooks(); + plugin.apply({ + hooks: hook, + logger: dummyLog(), + prefixRelease, + } as Auto.Auto); + + await hook.publish.promise({ bump: Auto.SEMVER.patch }); + + expect(exec).toBeCalledTimes(2); + expect(exec).lastCalledWith("notpod", [ + "trunk", + "push", + "./Test.podspec", + ]); + }); + + test("should push with different pod command with spaces if in options", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const plugin = new CocoapodsPlugin({ + ...options, + podCommand: "bundle exec pod", + }); + const hook = makeHooks(); + plugin.apply({ + hooks: hook, + logger: dummyLog(), + prefixRelease, + } as Auto.Auto); + + await hook.publish.promise({ bump: Auto.SEMVER.patch }); + + expect(exec).toBeCalledTimes(2); + expect(exec).lastCalledWith("bundle", [ + "exec", + "pod", + "trunk", + "push", + "./Test.podspec", + ]); + }); + + test("should push to trunk if no specsRepo in options with flags", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const logger = dummyLog(); + logger.logLevel = "verbose"; + + const plugin = new CocoapodsPlugin({ + ...options, + flags: ["--sources", "someOtherSpecsRepo"], + }); + const hook = makeHooks(); + plugin.apply({ + hooks: hook, + logger, + prefixRelease, + } as Auto.Auto); + + await hook.publish.promise({ bump: Auto.SEMVER.patch }); + + expect(exec).toBeCalledTimes(2); + expect(exec).lastCalledWith("pod", [ + "trunk", + "push", + "--sources", + "someOtherSpecsRepo", + "./Test.podspec", + "--verbose", + ]); + }); + + test("should push to specs repo if specsRepo in options", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const logger = dummyLog(); + logger.logLevel = "quiet"; + + const plugin = new CocoapodsPlugin({ + ...options, + specsRepo: "someSpecsRepo", + }); + const hook = makeHooks(); + plugin.apply({ + hooks: hook, + logger, + prefixRelease, + } as Auto.Auto); + + await hook.publish.promise({ bump: Auto.SEMVER.patch }); + + expect(exec).toBeCalledTimes(5); + expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); + expect(exec).toHaveBeenNthCalledWith(3, "pod", [ + "repo", + "add", + "autoPublishRepo", + "someSpecsRepo", + "--silent", + ]); + expect(exec).toHaveBeenNthCalledWith(4, "pod", [ + "repo", + "push", + "autoPublishRepo", + "./Test.podspec", + "--silent", + ]); + expect(exec).toHaveBeenNthCalledWith(5, "pod", [ + "repo", + "remove", + "autoPublishRepo", + "--silent", + ]); + }); + test("should push multiple podspecs to specs repo if specsRepo in options", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const logger = dummyLog(); + logger.logLevel = "quiet"; + + const plugin = new CocoapodsPlugin({ + ...multiOptions, + specsRepo: "someSpecsRepo", + }); + const hook = makeHooks(); + plugin.apply({ + hooks: hook, + logger, + prefixRelease, + } as Auto.Auto); + + await hook.publish.promise({ bump: Auto.SEMVER.patch }); + + expect(exec).toBeCalledTimes(6); + expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); + expect(exec).toHaveBeenNthCalledWith(3, "pod", [ + "repo", + "add", + "autoPublishRepo", + "someSpecsRepo", + "--silent", + ]); + expect(exec).toHaveBeenNthCalledWith(4, "pod", [ + "repo", + "push", + "autoPublishRepo", + "./Test.podspec", + "--silent", + ]); + expect(exec).toHaveBeenNthCalledWith(5, "pod", [ + "repo", + "push", + "autoPublishRepo", + "./Test2.podspec", + "--silent", + ]); + expect(exec).toHaveBeenNthCalledWith(6, "pod", [ + "repo", + "remove", + "autoPublishRepo", + "--silent", + ]); + }); + + test("should push to specs repo if specsRepo in options with flags", async () => { + mockPodspec(specWithVersion("0.0.1")); + + const logger = dummyLog(); + logger.logLevel = "veryVerbose"; + + const plugin = new CocoapodsPlugin({ + ...options, + specsRepo: "someSpecsRepo", + flags: ["--sources", "someOtherSpecsRepo"], + }); + const hook = makeHooks(); + plugin.apply({ + hooks: hook, + logger, + prefixRelease, + } as Auto.Auto); + + await hook.publish.promise({ bump: Auto.SEMVER.patch }); + + expect(exec).toBeCalledTimes(5); + expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); + expect(exec).toHaveBeenNthCalledWith(3, "pod", [ + "repo", + "add", + "autoPublishRepo", + "someSpecsRepo", + "--verbose", + ]); + expect(exec).toHaveBeenNthCalledWith(4, "pod", [ + "repo", + "push", + "--sources", + "someOtherSpecsRepo", + "autoPublishRepo", + "./Test.podspec", + "--verbose", + ]); + expect(exec).toHaveBeenNthCalledWith(5, "pod", [ + "repo", + "remove", + "autoPublishRepo", + "--verbose", + ]); + }); + + test("should delete autoPublishRepo if it exists and push to specs repo if specsRepo in options", async () => { + mockPodspec(specWithVersion("0.0.1")); + + exec = jest.fn().mockImplementation((...args) => { + if (args[1]?.[1] === "list") { + return ` +autoPublishRepo +- Type: git (master) +- URL: someSpecsRepo +- Path: /Users/someUser/.cocoapods/repos/autoPublishRepo + +master +- Type: git (master) +- URL: https://github.com/CocoaPods/Specs.git +- Path: /Users/someUser/.cocoapods/repos/master + +trunk +- Type: CDN +- URL: https://cdn.cocoapods.org/ +- Path: /Users/someUser/.cocoapods/repos/trunk + `; + } + }); + + const plugin = new CocoapodsPlugin({ + ...options, + specsRepo: "someSpecsRepo", + }); + const hook = makeHooks(); + plugin.apply({ + hooks: hook, + logger: dummyLog(), + prefixRelease, + } as Auto.Auto); + + await hook.publish.promise({ bump: Auto.SEMVER.patch }); + + expect(exec).toBeCalledTimes(6); + expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); + expect(exec).toHaveBeenNthCalledWith(3, "pod", [ + "repo", + "remove", + "autoPublishRepo", + ]); + expect(exec).toHaveBeenNthCalledWith(4, "pod", [ + "repo", + "add", + "autoPublishRepo", + "someSpecsRepo", + ]); + expect(exec).toHaveBeenNthCalledWith(5, "pod", [ + "repo", + "push", + "autoPublishRepo", + "./Test.podspec", + ]); + expect(exec).toHaveBeenNthCalledWith(6, "pod", [ + "repo", + "remove", + "autoPublishRepo", + ]); + }); + }); +}); diff --git a/plugins/fastlane/package.json b/plugins/fastlane/package.json new file mode 100644 index 000000000..f1b511969 --- /dev/null +++ b/plugins/fastlane/package.json @@ -0,0 +1,45 @@ +{ + "name": "@auto-it/fastlane", + "version": "10.36.5", + "main": "dist/index.js", + "description": "Use auto to version your pList using fastlane", + "license": "MIT", + "author": { + "name": "Yogesh Khandelwal", + "email": "yogesh_khandelwal@intuit.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" + ], + "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.1.3", + "tslib": "2.1.0" + } +} diff --git a/plugins/fastlane/src/index.ts b/plugins/fastlane/src/index.ts new file mode 100644 index 000000000..dd0a7a207 --- /dev/null +++ b/plugins/fastlane/src/index.ts @@ -0,0 +1,405 @@ +import { + Auto, + IPlugin, + execPromise, + validatePluginConfiguration, + ILogger, + getPrNumberFromEnv, + DEFAULT_PRERELEASE_BRANCHES, + getCurrentBranch, + determineNextVersion, +} from "@auto-it/core"; + +import { inc, ReleaseType } from "semver"; + +import * as t from "io-ts"; + +import { getpListContents, writepListContents } from "./utilities"; + +const logPrefix = "[Fastlane-Plugin]"; +const pListCommand = "/usr/libexec/PlistBuddy -c \"Print CFBundleShortVersionString\" " + +/** + * Wrapper to add logPrefix to messages + * + * @param msg - The message to add a prefix to + * @returns Log message with prefix prepended + */ +const logMessage = (msg: string): string => `${logPrefix} ${msg}`; + +const required = t.interface({ + /** Relative path to podspec file */ + pListPath: t.union([t.string, t.array(t.string)]), +}); + +const optional = t.partial({ + /** The fastlane repo to publish to */ + specsRepo: t.string, + + /** Any additional command line flags to pass to `fastlane increment_version_number` */ + flags: t.array(t.string), + + /** The command to use for `pod` if it needs to be separate like `bundle exec fastlane` */ + fastlaneCommand: t.string, +}); + +const pluginOptions = t.intersection([required, optional]); +export type IFastlanePluginOptions = t.TypeOf; + +/** + * Retrieves the version currently in the pList file + * + * @param pListPath - The relative path to the podspec file + */ +export function getVersion(pListPath: string): string { + const pListContents = getpListContents(pListPath); + return pListCommand.concat.pListContents; + + throw new Error(`Version could not be found in podspec: ${pListPath}`); +} + + +/** + * Updates the version in the podspec to the supplied version + * + * @param pListPath - The relative path to the podspec file + * @param version - The version to update the podspec to + */ +export function updatePodspecVersion(pListPath: string, version: string) { + const previousVersion = getVersion(pListPath); + const parsedContents = getParsedPodspecContents(pListPath); + const podspecContents = getPodspecContents(pListPath); + + try { + if (parsedContents?.[0]) { + const newVersionString = parsedContents[0].replace( + previousVersion, + version + ); + const newPodspec = podspecContents.replace( + versionRegex, + newVersionString + ); + + writePodspecContents(pListPath, newPodspec); + } + } catch (error) { + throw new Error(`Error updating version in podspec: ${pListPath}`); + } +} + +/** Use auto to version your pList */ +export default class FastlanePlugin implements IPlugin { + /** The name of the plugin */ + name = "fastlane"; + + /** The auto logger */ + logger?: ILogger; + + /** The options of the plugin */ + readonly options: IFastlanePluginOptions; + + /** + * + */ + private get paths() { + if (typeof this.options.pListPath === "string") { + return [this.options.pListPath]; + } + + return this.options.pListPath; + } + + /** Initialize the plugin with it's options */ + constructor(options: IFastlanePluginOptions) { + this.options = options; + } + + /** Tap into auto plugin points. */ + apply(auto: Auto) { + this.logger = auto.logger; + const isQuiet = auto.logger.logLevel === "quiet"; + const isVerbose = + auto.logger.logLevel === "verbose" || + auto.logger.logLevel === "veryVerbose"; + const fastlaneLogLevel = isQuiet ? ["--silent"] : isVerbose ? ["--verbose"] : []; + + auto.hooks.validateConfig.tapPromise(this.name, async (name, options) => { + if (name === this.name || name === `@auto-it/${this.name}`) { + return validatePluginConfiguration(this.name, pluginOptions, options); + } + }); + + auto.hooks.modifyConfig.tap(this.name, (config) => ({ + ...config, + noVersionPrefix: true, + })); + + auto.hooks.getPreviousVersion.tapPromise(this.name, async () => { + // Due to CocoaPods being git backed, all the versions will be the same + // so there are no git tag collisions + return auto.prefixRelease(getVersion(this.paths[0])); + }); + + auto.hooks.version.tapPromise( + this.name, + async ({ bump, dryRun, quiet }) => { + const previousVersion = getVersion(this.paths[0]); + const releaseVersion = inc(previousVersion, bump as ReleaseType); + if (dryRun && releaseVersion) { + if (quiet) { + console.log(releaseVersion); + } else { + auto.logger.log.info(`Would have published: ${releaseVersion}`); + } + + return; + } + + if (!releaseVersion) { + throw new Error( + `Could not increment previous version: ${previousVersion}` + ); + } + + this.paths.forEach((path) => { + updatePodspecVersion(path, releaseVersion); + }); + + await execPromise("git", [ + "commit", + "-am", + `"update version: ${releaseVersion} [skip ci]"`, + "--no-verify", + ]); + + await execPromise("git", [ + "tag", + `${releaseVersion}`, + "-m", + `"Update version to ${releaseVersion}"`, + ]); + } + ); + + auto.hooks.canary.tapPromise( + this.name, + async ({ bump, canaryIdentifier, dryRun, quiet }) => { + if (!auto.git) { + return; + } + + const pr = getPrNumberFromEnv(); + + if (!pr) { + this.logger?.log.info( + logMessage( + `No PR number found, using ${auto.remote} as the remote for canary. Commit must be pushed for this to work.` + ) + ); + } + + const remoteRepo = pr + ? (await auto.git.getPullRequest(pr)).data.head.repo?.clone_url || + auto.remote + : auto.remote; + + const lastRelease = await auto.git.getLatestRelease(); + const current = await auto.getCurrentVersion(lastRelease); + const nextVersion = inc(current, bump as ReleaseType); + const canaryVersion = `${nextVersion}-${canaryIdentifier}`; + + if (dryRun) { + if (quiet) { + console.log(canaryVersion); + } else { + auto.logger.log.info(`Would have published: ${canaryVersion}`); + } + + return; + } + + await this.paths.reduce( + (promise, path) => + promise.then(async () => { + await updateSourceLocation(path, remoteRepo); + + updatePodspecVersion(path, canaryVersion); + }), + Promise.resolve() + ); + + // Publish the canary podspec, committing it isn't needed for specs push + await this.publishPodSpec(fastlaneLogLevel); + + // Reset changes to podspec file since it doesn't need to be committed + await this.paths.reduce( + (promise, path) => + promise.then(async () => { + await execPromise("git", ["checkout", path]); + }), + Promise.resolve() + ); + + return canaryVersion; + } + ); + + auto.hooks.next.tapPromise( + this.name, + async (preReleaseVersions, { bump, dryRun }) => { + if (!auto.git) { + return preReleaseVersions; + } + + const prereleaseBranches = + auto.config?.prereleaseBranches ?? DEFAULT_PRERELEASE_BRANCHES; + const branch = getCurrentBranch() || ""; + const prereleaseBranch = prereleaseBranches.includes(branch) + ? branch + : prereleaseBranches[0]; + const lastRelease = await auto.git.getLatestRelease(); + const current = + (await auto.git.getLastTagNotInBaseBranch(prereleaseBranch)) || + (await auto.getCurrentVersion(lastRelease)); + const prerelease = determineNextVersion( + lastRelease, + current, + bump, + prereleaseBranch + ); + + preReleaseVersions.push(prerelease); + + if (dryRun) { + return preReleaseVersions; + } + + await execPromise("git", [ + "tag", + prerelease, + "-m", + `"Tag pre-release: ${prerelease}"`, + ]); + + await execPromise("git", ["push", auto.remote, branch, "--tags"]); + + this.paths.forEach((path) => updatePodspecVersion(path, prerelease)); + + // Publish the next podspec, committing it isn't needed for specs push + await this.publishPodSpec(fastlaneLogLevel); + + // Reset changes to podspec file since it doesn't need to be committed + await Promise.all( + this.paths.map((path) => execPromise("git", ["checkout", path])) + ); + + return preReleaseVersions; + } + ); + + auto.hooks.publish.tapPromise(this.name, async () => { + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", + auto.remote, + auto.baseBranch, + ]); + await this.publishPodSpec(fastlaneLogLevel); + }); + } + + /** + * + */ + async publishPodSpec(fastlaneLogLevel: string[]) { + const [fastlane, ...commands] = this.options.fastlaneCommand?.split(" ") || ["fastlane"]; + if (!this.options.specsRepo) { + this.logger?.log.info(logMessage(`Pushing to Cocoapods trunk`)); + await this.paths.reduce( + (promise, path) => + promise.then(() => + execPromise(fastlane, [ + ...commands, + "trunk", + "push", + ...(this.options.flags || []), + path, + ...fastlaneLogLevel, + ]) + ), + Promise.resolve("") + ); + return; + } + + try { + const existingRepos = await execPromise(fastlane, [ + ...commands, + "repo", + "list", + ]); + if (existingRepos.indexOf("autoPublishRepo") !== -1) { + this.logger?.log.info("Removing existing autoPublishRepo"); + await execPromise(fastlane, [ + ...commands, + "repo", + "remove", + "autoPublishRepo", + ...fastlaneLogLevel, + ]); + } + } catch (error) { + this.logger?.log.warn( + `Error Checking for existing Specs repositories: ${error}` + ); + } + + try { + await execPromise(fastlane, [ + ...commands, + "repo", + "add", + "autoPublishRepo", + this.options.specsRepo, + ...fastlaneLogLevel, + ]); + + this.logger?.log.info( + logMessage(`Pushing to specs repo: ${this.options.specsRepo}`) + ); + + await this.paths.reduce( + (promise, path) => + promise.then(() => + execPromise(fastlane, [ + ...commands, + "repo", + "push", + ...(this.options.flags || []), + "autoPublishRepo", + path, + ...fastlaneLogLevel, + ]) + ), + Promise.resolve("") + ); + } catch (error) { + this.logger?.log.error( + logMessage( + `Error pushing to specs repo: ${this.options.specsRepo}. Error: ${error}` + ) + ); + process.exit(1); + } finally { + await execPromise(fastlane, [ + ...commands, + "repo", + "remove", + "autoPublishRepo", + ...fastlaneLogLevel, + ]); + } + } +} diff --git a/plugins/fastlane/src/utilities.ts b/plugins/fastlane/src/utilities.ts new file mode 100644 index 000000000..f9f8a6a58 --- /dev/null +++ b/plugins/fastlane/src/utilities.ts @@ -0,0 +1,22 @@ +import path from "path"; +import fs from "fs"; + +/** + * Retrieves the contents of the podspec file + * + * @param pListPath - The relative path to the podspec file + * @returns A string that is the contents of the file + */ +export function getpListContents(pListPath: string): string { + return fs.readFileSync(path.join(process.cwd(), pListPath)).toString(); +} + +/** + * Write the podspec file contents + * + * @param pListPath - The relative path to the podspec file + * @param contents - The contents to write to the podspec path + */ +export function writepListContents(pListPath: string, contents: string) { + fs.writeFileSync(path.join(process.cwd(), pListPath), contents); +} diff --git a/plugins/fastlane/tsconfig.json b/plugins/fastlane/tsconfig.json new file mode 100644 index 000000000..bfbef6fc7 --- /dev/null +++ b/plugins/fastlane/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "include": ["src/**/*", "../../typings/**/*"], + + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src", + "composite": true + }, + + "references": [ + { + "path": "../../packages/core" + } + ] +} From 5f6dd6fa9c6a48981b44723db0f9a0ee61f080b9 Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Wed, 4 May 2022 00:18:55 +0530 Subject: [PATCH 02/12] updating version function --- plugins/fastlane/src/index.ts | 44 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/plugins/fastlane/src/index.ts b/plugins/fastlane/src/index.ts index dd0a7a207..7b98d21da 100644 --- a/plugins/fastlane/src/index.ts +++ b/plugins/fastlane/src/index.ts @@ -17,7 +17,6 @@ import * as t from "io-ts"; import { getpListContents, writepListContents } from "./utilities"; const logPrefix = "[Fastlane-Plugin]"; -const pListCommand = "/usr/libexec/PlistBuddy -c \"Print CFBundleShortVersionString\" " /** * Wrapper to add logPrefix to messages @@ -53,35 +52,36 @@ export type IFastlanePluginOptions = t.TypeOf; */ export function getVersion(pListPath: string): string { const pListContents = getpListContents(pListPath); - return pListCommand.concat.pListContents; + return execPromise("/usr/libexec/PlistBuddy", [ + "-c", + `"Print CFBundleShortVersionString"`, + pListContents, + ]); throw new Error(`Version could not be found in podspec: ${pListPath}`); } /** - * Updates the version in the podspec to the supplied version + * Updates the version in the pList to the supplied version * - * @param pListPath - The relative path to the podspec file - * @param version - The version to update the podspec to + * @param pListPath - The relative path to the pList file + * @param version - The version to update the pList to */ -export function updatePodspecVersion(pListPath: string, version: string) { - const previousVersion = getVersion(pListPath); - const parsedContents = getParsedPodspecContents(pListPath); - const podspecContents = getPodspecContents(pListPath); - +export function updatepListVersion(pListPath: string, version: string) { + const parsedpListContents = getpListContents(pListPath); try { - if (parsedContents?.[0]) { - const newVersionString = parsedContents[0].replace( - previousVersion, - version - ); - const newPodspec = podspecContents.replace( - versionRegex, - newVersionString - ); - - writePodspecContents(pListPath, newPodspec); + if (parsedpListContents?.[0]) { + execPromise("bundle", [ + "exec", + "fastlane", + version, + ]); + const newVersionString = execPromise("/usr/libexec/PlistBuddy", [ + "-c", + `"Print CFBundleShortVersionString"`, + parsedpListContents, + ]); } } catch (error) { throw new Error(`Error updating version in podspec: ${pListPath}`); @@ -163,7 +163,7 @@ export default class FastlanePlugin implements IPlugin { } this.paths.forEach((path) => { - updatePodspecVersion(path, releaseVersion); + updatepListVersion(path, releaseVersion); }); await execPromise("git", [ From 1691b2cd1e6c3388d75a74677d63d9d40c3d7ce6 Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Wed, 4 May 2022 00:22:15 +0530 Subject: [PATCH 03/12] Updating function --- plugins/fastlane/src/index.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/fastlane/src/index.ts b/plugins/fastlane/src/index.ts index 7b98d21da..280abd15b 100644 --- a/plugins/fastlane/src/index.ts +++ b/plugins/fastlane/src/index.ts @@ -50,9 +50,9 @@ export type IFastlanePluginOptions = t.TypeOf; * * @param pListPath - The relative path to the podspec file */ -export function getVersion(pListPath: string): string { +export async function getVersion(pListPath: string): string { const pListContents = getpListContents(pListPath); - return execPromise("/usr/libexec/PlistBuddy", [ + return await execPromise ("/usr/libexec/PlistBuddy", [ "-c", `"Print CFBundleShortVersionString"`, pListContents, @@ -68,16 +68,16 @@ export function getVersion(pListPath: string): string { * @param pListPath - The relative path to the pList file * @param version - The version to update the pList to */ -export function updatepListVersion(pListPath: string, version: string) { +export async function updatepListVersion(pListPath: string, version: string) { const parsedpListContents = getpListContents(pListPath); try { if (parsedpListContents?.[0]) { - execPromise("bundle", [ + await execPromise("bundle", [ "exec", "fastlane", version, ]); - const newVersionString = execPromise("/usr/libexec/PlistBuddy", [ + const newVersionString = await execPromise("/usr/libexec/PlistBuddy", [ "-c", `"Print CFBundleShortVersionString"`, parsedpListContents, From 9f0d6f395242e1a1880ff937ac797911020964c5 Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Wed, 4 May 2022 00:24:47 +0530 Subject: [PATCH 04/12] Updating function --- plugins/fastlane/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/fastlane/src/index.ts b/plugins/fastlane/src/index.ts index 280abd15b..7239078c7 100644 --- a/plugins/fastlane/src/index.ts +++ b/plugins/fastlane/src/index.ts @@ -50,7 +50,7 @@ export type IFastlanePluginOptions = t.TypeOf; * * @param pListPath - The relative path to the podspec file */ -export async function getVersion(pListPath: string): string { +export async function getVersion(pListPath: string): Promise { const pListContents = getpListContents(pListPath); return await execPromise ("/usr/libexec/PlistBuddy", [ "-c", From 889dc6999df953d62b1298a7fd25c3eb892dcc31 Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Wed, 4 May 2022 00:37:53 +0530 Subject: [PATCH 05/12] removing updatePlist as it will be automatic --- plugins/fastlane/src/index.ts | 46 +++++++---------------------------- 1 file changed, 9 insertions(+), 37 deletions(-) diff --git a/plugins/fastlane/src/index.ts b/plugins/fastlane/src/index.ts index 7239078c7..a4aa912e4 100644 --- a/plugins/fastlane/src/index.ts +++ b/plugins/fastlane/src/index.ts @@ -32,14 +32,10 @@ const required = t.interface({ }); const optional = t.partial({ - /** The fastlane repo to publish to */ - specsRepo: t.string, - - /** Any additional command line flags to pass to `fastlane increment_version_number` */ - flags: t.array(t.string), - - /** The command to use for `pod` if it needs to be separate like `bundle exec fastlane` */ + /** The command to use for `fastlane` if it needs to be separate like `fastlane action increment_version_numer` */ fastlaneCommand: t.string, + /** xcode project path */ + xcodeproj: t.string, }); const pluginOptions = t.intersection([required, optional]); @@ -61,33 +57,6 @@ export async function getVersion(pListPath: string): Promise { throw new Error(`Version could not be found in podspec: ${pListPath}`); } - -/** - * Updates the version in the pList to the supplied version - * - * @param pListPath - The relative path to the pList file - * @param version - The version to update the pList to - */ -export async function updatepListVersion(pListPath: string, version: string) { - const parsedpListContents = getpListContents(pListPath); - try { - if (parsedpListContents?.[0]) { - await execPromise("bundle", [ - "exec", - "fastlane", - version, - ]); - const newVersionString = await execPromise("/usr/libexec/PlistBuddy", [ - "-c", - `"Print CFBundleShortVersionString"`, - parsedpListContents, - ]); - } - } catch (error) { - throw new Error(`Error updating version in podspec: ${pListPath}`); - } -} - /** Use auto to version your pList */ export default class FastlanePlugin implements IPlugin { /** The name of the plugin */ @@ -162,9 +131,12 @@ export default class FastlanePlugin implements IPlugin { ); } - this.paths.forEach((path) => { - updatepListVersion(path, releaseVersion); - }); + await execPromise("fastlane", [ + "action", + "increment_version_number", + `version_number:${releaseVersion}`, + `xcodeproj:${this.options.xcodeproj}`, + ]) await execPromise("git", [ "commit", From 307c628b33e3662b42e4071c17494f42c154f9df Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Wed, 4 May 2022 00:49:52 +0530 Subject: [PATCH 06/12] Added releaseType support --- plugins/fastlane/src/index.ts | 223 ------------------------------ plugins/fastlane/src/utilities.ts | 10 -- 2 files changed, 233 deletions(-) diff --git a/plugins/fastlane/src/index.ts b/plugins/fastlane/src/index.ts index a4aa912e4..872324b73 100644 --- a/plugins/fastlane/src/index.ts +++ b/plugins/fastlane/src/index.ts @@ -105,8 +105,6 @@ export default class FastlanePlugin implements IPlugin { })); auto.hooks.getPreviousVersion.tapPromise(this.name, async () => { - // Due to CocoaPods being git backed, all the versions will be the same - // so there are no git tag collisions return auto.prefixRelease(getVersion(this.paths[0])); }); @@ -153,225 +151,4 @@ export default class FastlanePlugin implements IPlugin { ]); } ); - - auto.hooks.canary.tapPromise( - this.name, - async ({ bump, canaryIdentifier, dryRun, quiet }) => { - if (!auto.git) { - return; - } - - const pr = getPrNumberFromEnv(); - - if (!pr) { - this.logger?.log.info( - logMessage( - `No PR number found, using ${auto.remote} as the remote for canary. Commit must be pushed for this to work.` - ) - ); - } - - const remoteRepo = pr - ? (await auto.git.getPullRequest(pr)).data.head.repo?.clone_url || - auto.remote - : auto.remote; - - const lastRelease = await auto.git.getLatestRelease(); - const current = await auto.getCurrentVersion(lastRelease); - const nextVersion = inc(current, bump as ReleaseType); - const canaryVersion = `${nextVersion}-${canaryIdentifier}`; - - if (dryRun) { - if (quiet) { - console.log(canaryVersion); - } else { - auto.logger.log.info(`Would have published: ${canaryVersion}`); - } - - return; - } - - await this.paths.reduce( - (promise, path) => - promise.then(async () => { - await updateSourceLocation(path, remoteRepo); - - updatePodspecVersion(path, canaryVersion); - }), - Promise.resolve() - ); - - // Publish the canary podspec, committing it isn't needed for specs push - await this.publishPodSpec(fastlaneLogLevel); - - // Reset changes to podspec file since it doesn't need to be committed - await this.paths.reduce( - (promise, path) => - promise.then(async () => { - await execPromise("git", ["checkout", path]); - }), - Promise.resolve() - ); - - return canaryVersion; - } - ); - - auto.hooks.next.tapPromise( - this.name, - async (preReleaseVersions, { bump, dryRun }) => { - if (!auto.git) { - return preReleaseVersions; - } - - const prereleaseBranches = - auto.config?.prereleaseBranches ?? DEFAULT_PRERELEASE_BRANCHES; - const branch = getCurrentBranch() || ""; - const prereleaseBranch = prereleaseBranches.includes(branch) - ? branch - : prereleaseBranches[0]; - const lastRelease = await auto.git.getLatestRelease(); - const current = - (await auto.git.getLastTagNotInBaseBranch(prereleaseBranch)) || - (await auto.getCurrentVersion(lastRelease)); - const prerelease = determineNextVersion( - lastRelease, - current, - bump, - prereleaseBranch - ); - - preReleaseVersions.push(prerelease); - - if (dryRun) { - return preReleaseVersions; - } - - await execPromise("git", [ - "tag", - prerelease, - "-m", - `"Tag pre-release: ${prerelease}"`, - ]); - - await execPromise("git", ["push", auto.remote, branch, "--tags"]); - - this.paths.forEach((path) => updatePodspecVersion(path, prerelease)); - - // Publish the next podspec, committing it isn't needed for specs push - await this.publishPodSpec(fastlaneLogLevel); - - // Reset changes to podspec file since it doesn't need to be committed - await Promise.all( - this.paths.map((path) => execPromise("git", ["checkout", path])) - ); - - return preReleaseVersions; - } - ); - - auto.hooks.publish.tapPromise(this.name, async () => { - await execPromise("git", [ - "push", - "--follow-tags", - "--set-upstream", - auto.remote, - auto.baseBranch, - ]); - await this.publishPodSpec(fastlaneLogLevel); - }); } - - /** - * - */ - async publishPodSpec(fastlaneLogLevel: string[]) { - const [fastlane, ...commands] = this.options.fastlaneCommand?.split(" ") || ["fastlane"]; - if (!this.options.specsRepo) { - this.logger?.log.info(logMessage(`Pushing to Cocoapods trunk`)); - await this.paths.reduce( - (promise, path) => - promise.then(() => - execPromise(fastlane, [ - ...commands, - "trunk", - "push", - ...(this.options.flags || []), - path, - ...fastlaneLogLevel, - ]) - ), - Promise.resolve("") - ); - return; - } - - try { - const existingRepos = await execPromise(fastlane, [ - ...commands, - "repo", - "list", - ]); - if (existingRepos.indexOf("autoPublishRepo") !== -1) { - this.logger?.log.info("Removing existing autoPublishRepo"); - await execPromise(fastlane, [ - ...commands, - "repo", - "remove", - "autoPublishRepo", - ...fastlaneLogLevel, - ]); - } - } catch (error) { - this.logger?.log.warn( - `Error Checking for existing Specs repositories: ${error}` - ); - } - - try { - await execPromise(fastlane, [ - ...commands, - "repo", - "add", - "autoPublishRepo", - this.options.specsRepo, - ...fastlaneLogLevel, - ]); - - this.logger?.log.info( - logMessage(`Pushing to specs repo: ${this.options.specsRepo}`) - ); - - await this.paths.reduce( - (promise, path) => - promise.then(() => - execPromise(fastlane, [ - ...commands, - "repo", - "push", - ...(this.options.flags || []), - "autoPublishRepo", - path, - ...fastlaneLogLevel, - ]) - ), - Promise.resolve("") - ); - } catch (error) { - this.logger?.log.error( - logMessage( - `Error pushing to specs repo: ${this.options.specsRepo}. Error: ${error}` - ) - ); - process.exit(1); - } finally { - await execPromise(fastlane, [ - ...commands, - "repo", - "remove", - "autoPublishRepo", - ...fastlaneLogLevel, - ]); - } - } -} diff --git a/plugins/fastlane/src/utilities.ts b/plugins/fastlane/src/utilities.ts index f9f8a6a58..e46baa729 100644 --- a/plugins/fastlane/src/utilities.ts +++ b/plugins/fastlane/src/utilities.ts @@ -10,13 +10,3 @@ import fs from "fs"; export function getpListContents(pListPath: string): string { return fs.readFileSync(path.join(process.cwd(), pListPath)).toString(); } - -/** - * Write the podspec file contents - * - * @param pListPath - The relative path to the podspec file - * @param contents - The contents to write to the podspec path - */ -export function writepListContents(pListPath: string, contents: string) { - fs.writeFileSync(path.join(process.cwd(), pListPath), contents); -} From 8e61350b79d0b966c4adfe7bbfb37438ac14d3f3 Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Wed, 4 May 2022 00:54:01 +0530 Subject: [PATCH 07/12] Addding publish --- plugins/fastlane/src/index.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/fastlane/src/index.ts b/plugins/fastlane/src/index.ts index 872324b73..acb30bd09 100644 --- a/plugins/fastlane/src/index.ts +++ b/plugins/fastlane/src/index.ts @@ -151,4 +151,14 @@ export default class FastlanePlugin implements IPlugin { ]); } ); + + auto.hooks.publish.tapPromise(this.name, async () => { + await execPromise("git", [ + "push", + "--follow-tags", + "--set-upstream", + auto.remote, + auto.baseBranch, + ]); + }); } From 0c8444da040ddd539dbbed7d4d19e49a70b4d60b Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Wed, 4 May 2022 00:56:29 +0530 Subject: [PATCH 08/12] moving fastlane to required --- plugins/fastlane/src/index.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/fastlane/src/index.ts b/plugins/fastlane/src/index.ts index acb30bd09..1c0a65212 100644 --- a/plugins/fastlane/src/index.ts +++ b/plugins/fastlane/src/index.ts @@ -29,13 +29,14 @@ const logMessage = (msg: string): string => `${logPrefix} ${msg}`; const required = t.interface({ /** Relative path to podspec file */ pListPath: t.union([t.string, t.array(t.string)]), + /** The command to use for `fastlane` if it needs to be separate like `fastlane action increment_version_numer` */ + fastlaneCommand: t.string, }); const optional = t.partial({ - /** The command to use for `fastlane` if it needs to be separate like `fastlane action increment_version_numer` */ - fastlaneCommand: t.string, - /** xcode project path */ - xcodeproj: t.string, + /** xcode project path */ + xcodeproj: t.string, + }); const pluginOptions = t.intersection([required, optional]); From 4eeecea5eedf219266789354f01d13add3282bb7 Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Thu, 5 May 2022 09:52:27 +0530 Subject: [PATCH 09/12] Disabling test --- plugins/fastlane/__tests__/cocoapods.test.ts | 1038 ------------------ plugins/fastlane/__tests__/fastlane.test.ts | 1035 +++++++++++++++++ 2 files changed, 1035 insertions(+), 1038 deletions(-) delete mode 100644 plugins/fastlane/__tests__/cocoapods.test.ts create mode 100644 plugins/fastlane/__tests__/fastlane.test.ts diff --git a/plugins/fastlane/__tests__/cocoapods.test.ts b/plugins/fastlane/__tests__/cocoapods.test.ts deleted file mode 100644 index bd25574b6..000000000 --- a/plugins/fastlane/__tests__/cocoapods.test.ts +++ /dev/null @@ -1,1038 +0,0 @@ -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 * as utilities from "../src/utilities"; -import CocoapodsPlugin, { - ICocoapodsPluginOptions, - getParsedPodspecContents, - getVersion, - updatePodspecVersion, - updateSourceLocation, - getSourceInfo, -} from "../src"; - -const specWithVersion = ( - version: string, - source = "{ :git => 'https://github.com/intuit/auto.git', :tag => s.version.to_s }" -) => ` - Pod:: Spec.new do | s | - s.name = 'Test' - s.version = '${version}' - s.summary = 'A short description of Test.' - - # This description is used to generate tags and improve search results. - # * Think: What does it do? Why did you write it ? What is the focus ? - # * Try to keep it short, snappy and to the point. - # * Write the description between the DESC delimiters below. - # * Finally, don't worry about the indent, CocoaPods strips it! - - s.description = << -DESC - TODO: Add long description of the pod here. - DESC - - s.homepage = 'https://github.com/intuit/auto' - # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' - s.license = { : type => 'MIT', : file => 'LICENSE' } - s.author = { 'hborawski' => 'harris_borawski@intuit.com' } - s.source = ${source} - - s.ios.deployment_target = '11.0' - - s.source_files = 'Test/Classes/**/*' - - - # s.public_header_files = 'Pod/Classes/**/*.h' - # s.frameworks = 'UIKit', 'MapKit' - # s.dependency 'Alamofire' - end - `; - -const mockPodspec = (contents: string) => { - return jest.spyOn(utilities, "getPodspecContents").mockReturnValue(contents); -}; - -interface FakePodspec { - path: string; - contents: string; -} -const mockPodspecs = (contents: FakePodspec[]) => { - return jest - .spyOn(utilities, "getPodspecContents") - .mockImplementation((path) => { - return contents.find((podspec) => podspec.path === path).contents; - }); -}; - -let exec = jest.fn(); -// @ts-ignore -jest.mock("../../../packages/core/dist/utils/exec-promise", () => (...args) => - exec(...args) -); -const logger = dummyLog(); - -describe("Cocoapods Plugin", () => { - let hooks: Auto.IAutoHooks; - let multiHooks: Auto.IAutoHooks; - const prefixRelease: (a: string) => string = (version: string) => { - return `v${version}`; - }; - - const options: ICocoapodsPluginOptions = { - podspecPath: "./Test.podspec", - }; - - const multiOptions: ICocoapodsPluginOptions = { - podspecPath: ["./Test.podspec", "./Test2.podspec"], - }; - - beforeEach(() => { - jest.resetAllMocks(); - exec.mockClear(); - const plugin = new CocoapodsPlugin(options); - const multiPlugin = new CocoapodsPlugin(multiOptions); - hooks = makeHooks(); - multiHooks = makeHooks(); - const apply = (p: CocoapodsPlugin, h: Auto.IAutoHooks) => { - p.apply(({ - hooks: h, - logger: logger, - prefixRelease, - git: { - getLastTagNotInBaseBranch: async () => undefined, - getLatestRelease: async () => "0.0.1", - getPullRequest: async () => ({ - data: { - head: { - repo: { - clone_url: "https://github.com/intuit-fork/auto.git", - }, - }, - }, - }), - }, - remote: "https://github.com/intuit/auto.git", - getCurrentVersion: async () => "0.0.1", - } as unknown) as Auto.Auto); - }; - - apply(plugin, hooks); - apply(multiPlugin, multiHooks); - }); - - describe("getParsedPodspecContents", () => { - test("should return null if contents cant be parsed with regex", () => { - mockPodspec("bad podspec"); - - expect(getParsedPodspecContents("./Test.podspec")).toBeNull(); - }); - test("should return parsed contents", () => { - mockPodspec(specWithVersion("0.0.1")); - const contents = getParsedPodspecContents("./Test.podspec"); - expect(contents).toHaveProperty("groups", { version: "0.0.1" }); - }); - }); - describe("getVersion", () => { - test("should throw error if parsed podspec is returned as null", () => { - mockPodspec("bad podspec"); - - expect(() => getVersion("./Test.podspec")).toThrow(); - }); - test("should return version", () => { - mockPodspec(specWithVersion("0.0.1")); - - expect(getVersion("./Test.podspec")).toBe("0.0.1"); - }); - test("should return canary version", () => { - mockPodspec(specWithVersion("0.0.1-canary.1.0.0")); - - expect(getVersion("./Test.podspec")).toBe("0.0.1-canary.1.0.0"); - }); - }); - describe("getSourceInfo", () => { - test("should throw error if source line cant be found", () => { - mockPodspec(specWithVersion("0.0.1", "no source")); - - expect(() => getSourceInfo("./Test.podspec")).toThrow(); - }); - test("should retrieve source info", () => { - mockPodspec(specWithVersion("0.0.1")); - - expect(getSourceInfo("./Test.podspec")).toBe( - "{ :git => 'https://github.com/intuit/auto.git', :tag => s.version.to_s }" - ); - }); - }); - describe("updatePodspecVersion", () => { - test("should throw error if there is an error writing file", async () => { - mockPodspec(specWithVersion("0.0.1")); - - jest - .spyOn(utilities, "writePodspecContents") - .mockImplementationOnce(() => { - throw new Error("Filesystem Error"); - }); - - expect( - updatePodspecVersion.bind(null, "./Test.podspec", "0.0.2") - ).toThrowError("Error updating version in podspec: ./Test.podspec"); - }); - test("should successfully write new version", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const mock = jest.spyOn(utilities, "writePodspecContents"); - - await updatePodspecVersion("./Test.podspec", "0.0.2"); - expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); - }); - }); - describe("updateSourceLocation", () => { - test("should throw error if there is an error writing file", async () => { - mockPodspec(specWithVersion("0.0.1")); - - exec.mockReturnValue("commithash"); - - jest - .spyOn(utilities, "writePodspecContents") - .mockImplementationOnce(() => { - throw new Error("Filesystem Error"); - }); - - await expect( - updateSourceLocation( - "./Test.podspec", - "https://github.com/somefork/auto.git" - ) - ).rejects.toThrowError( - "Error updating source location in podspec: ./Test.podspec" - ); - }); - test("should successfully write new source location", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const mock = jest.spyOn(utilities, "writePodspecContents"); - - exec.mockReturnValue(Promise.resolve("commithash")); - - await updateSourceLocation( - "./Test.podspec", - "https://github.com/somefork/auto.git" - ); - expect(mock).lastCalledWith( - expect.any(String), - specWithVersion( - "0.0.1", - "{ :git => 'https://github.com/somefork/auto.git', :commit => 'commithash' }" - ) - ); - }); - }); - describe("validateConfig hook", () => { - test("should validate options", async () => { - expect( - ((await hooks.validateConfig.promise("cocoapods", {})) || []) - ).toHaveLength(1); - expect( - ((await hooks.validateConfig.promise("cocoapods", options)) || []) - - ).toHaveLength(0); - expect( - ((await hooks.validateConfig.promise("cocoapods", multiOptions)) || []) - - ).toHaveLength(0); - }); - }); - describe("modifyConfig hook", () => { - test("should set noVersionPrefix to true", async () => { - const config = {}; - expect(await hooks.modifyConfig.promise(config as any)).toStrictEqual({ - noVersionPrefix: true, - }); - }); - }); - describe("getPreviousVersion hook", () => { - test("should get previous version from podspec", async () => { - mockPodspec(specWithVersion("0.0.1")); - - expect(await hooks.getPreviousVersion.promise()).toBe("v0.0.1"); - }); - - test("should throw if no version found", async () => { - mockPodspec(specWithVersion("")); - - await expect(hooks.getPreviousVersion.promise()).rejects.toThrowError( - "Version could not be found in podspec: ./Test.podspec" - ); - }); - - test("should get version if multiple podspecs", async () => { - mockPodspecs([ - { - path: "./Test.podspec", - contents: specWithVersion("0.0.1"), - }, - { - path: "./Test2.podspec", - contents: specWithVersion("0.0.1"), - }, - ]); - expect(await multiHooks.getPreviousVersion.promise()).toBe("v0.0.1"); - }); - }); - describe("version hook", () => { - test("should do nothing on dryRun", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const mockLog = jest.spyOn(logger.log, "info"); - - await hooks.version.promise({ bump: Auto.SEMVER.patch, dryRun: true }); - - expect(exec).toHaveBeenCalledTimes(0); - expect(mockLog).toHaveBeenCalledTimes(1); - }); - test("should not use logger on quiet dryRun", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const mockLog = jest.spyOn(logger.log, "info"); - const mockConsole = jest.spyOn(console, "log"); - - await hooks.version.promise({ - bump: Auto.SEMVER.patch, - dryRun: true, - quiet: true, - }); - - expect(exec).toHaveBeenCalledTimes(0); - expect(mockLog).toHaveBeenCalledTimes(0); - expect(mockConsole).toHaveBeenCalledTimes(1); - }); - test("should version release - patch version", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const mock = jest.spyOn(utilities, "writePodspecContents"); - - await hooks.version.promise({ bump: Auto.SEMVER.patch }); - - expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); - }); - test("should version release - minor version", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const mock = jest.spyOn(utilities, "writePodspecContents"); - - await hooks.version.promise({ bump: Auto.SEMVER.minor }); - - expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.1.0")); - }); - test("should version release - major version", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const mock = jest.spyOn(utilities, "writePodspecContents"); - - await hooks.version.promise({ bump: Auto.SEMVER.major }); - - expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); - }); - test("should release version multiple podspecs - patch", async () => { - mockPodspecs([ - { - path: "./Test.podspec", - contents: specWithVersion("0.0.1"), - }, - { - path: "./Test2.podspec", - contents: specWithVersion("0.0.1"), - }, - ]); - const mock = jest.spyOn(utilities, "writePodspecContents"); - await multiHooks.version.promise({ bump: Auto.SEMVER.patch }); - expect(mock).toHaveBeenCalledTimes(2); - expect(mock).toHaveBeenNthCalledWith( - 1, - "./Test.podspec", - specWithVersion("0.0.2") - ); - expect(mock).toHaveBeenNthCalledWith( - 2, - "./Test2.podspec", - specWithVersion("0.0.2") - ); - }); - test("should release version multiple podspecs - minor", async () => { - mockPodspecs([ - { - path: "./Test.podspec", - contents: specWithVersion("0.0.1"), - }, - { - path: "./Test2.podspec", - contents: specWithVersion("0.0.1"), - }, - ]); - const mock = jest.spyOn(utilities, "writePodspecContents"); - await multiHooks.version.promise({ bump: Auto.SEMVER.minor }); - expect(mock).toHaveBeenCalledTimes(2); - expect(mock).toHaveBeenNthCalledWith( - 1, - "./Test.podspec", - specWithVersion("0.1.0") - ); - expect(mock).toHaveBeenNthCalledWith( - 2, - "./Test2.podspec", - specWithVersion("0.1.0") - ); - }); - test("should release version multiple podspecs - major", async () => { - mockPodspecs([ - { - path: "./Test.podspec", - contents: specWithVersion("0.0.1"), - }, - { - path: "./Test2.podspec", - contents: specWithVersion("0.0.1"), - }, - ]); - const mock = jest.spyOn(utilities, "writePodspecContents"); - await multiHooks.version.promise({ bump: Auto.SEMVER.major }); - expect(mock).toHaveBeenCalledTimes(2); - expect(mock).toHaveBeenNthCalledWith( - 1, - "./Test.podspec", - specWithVersion("1.0.0") - ); - expect(mock).toHaveBeenNthCalledWith( - 2, - "./Test2.podspec", - specWithVersion("1.0.0") - ); - }); - test("should throw if there is an error writing new version", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const mock = jest - .spyOn(utilities, "writePodspecContents") - .mockImplementation((path, contents) => { - if (contents.includes("1.0.0")) { - throw new Error("Filesystem Error"); - } - }); - - await expect( - hooks.version.promise({ bump: Auto.SEMVER.major }) - ).rejects.toThrowError( - "Error updating version in podspec: ./Test.podspec" - ); - - expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); - }); - }); - - describe("beforeShipit hook", () => { - test("should call pod lib lint with dryRun flag", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const plugin = new CocoapodsPlugin(options); - const hook = makeHooks(); - - plugin.apply({ - hooks: hook, - logger: dummyLog(), - prefixRelease, - } as Auto.Auto); - - await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); - - expect(exec).toBeCalledTimes(1); - expect(exec).lastCalledWith("pod", ["lib", "lint", "./Test.podspec"]); - }); - test("should call pod lib lint for each podspec with dryRun flag", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const plugin = new CocoapodsPlugin(multiOptions); - const hook = makeHooks(); - - plugin.apply({ - hooks: hook, - logger: dummyLog(), - prefixRelease, - } as Auto.Auto); - - await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); - - expect(exec).toBeCalledTimes(2); - expect(exec).toHaveBeenNthCalledWith(1, "pod", [ - "lib", - "lint", - "./Test.podspec", - ]); - expect(exec).toHaveBeenNthCalledWith(2, "pod", [ - "lib", - "lint", - "./Test2.podspec", - ]); - }); - test("should call pod lib lint with options with dryRun flag", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const plugin = new CocoapodsPlugin({ - ...options, - flags: ["--flag"], - podCommand: "notpod", - }); - const hook = makeHooks(); - - plugin.apply({ - hooks: hook, - logger: dummyLog(), - prefixRelease, - } as Auto.Auto); - - await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); - - expect(exec).toBeCalledTimes(1); - expect(exec).lastCalledWith("notpod", [ - "lib", - "lint", - "--flag", - "./Test.podspec", - ]); - }); - }); - - describe("canary hook", () => { - test("should do nothing on dryRun", async () => { - mockPodspec(specWithVersion("0.0.1")); - jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); - - const mockLog = jest.spyOn(logger.log, "info"); - - await hooks.canary.promise({ - bump: Auto.SEMVER.patch, - canaryIdentifier: "canary.1.0", - dryRun: true, - }); - - expect(exec).toHaveBeenCalledTimes(0); - expect(mockLog).toHaveBeenCalledTimes(1); - }); - test("should not use logger on quiet dryRun", async () => { - mockPodspec(specWithVersion("0.0.1")); - jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); - - const mockLog = jest.spyOn(logger.log, "info"); - const mockConsole = jest.spyOn(console, "log"); - - await hooks.canary.promise({ - bump: Auto.SEMVER.patch, - canaryIdentifier: "canary.1.0", - dryRun: true, - quiet: true, - }); - - expect(exec).toHaveBeenCalledTimes(0); - expect(mockLog).toHaveBeenCalledTimes(0); - expect(mockConsole).toHaveBeenCalledTimes(1); - }); - test("should tag with canary version", async () => { - jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); - let podSpec = specWithVersion("0.0.1"); - jest - .spyOn(utilities, "getPodspecContents") - .mockImplementation(() => podSpec); - const mock = jest - .spyOn(utilities, "writePodspecContents") - .mockImplementation((path, contents) => { - podSpec = contents; - }); - - const newVersion = await hooks.canary.promise({ - bump: "minor" as Auto.SEMVER, - canaryIdentifier: "canary.1.1.1", - }); - - expect(newVersion).toBe("0.1.0-canary.1.1.1"); - expect(exec).toBeCalledTimes(3); - expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); - - expect(mock).toHaveBeenLastCalledWith( - expect.any(String), - specWithVersion( - "0.1.0-canary.1.1.1", - "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" - ) - ); - }); - test("should tag multiple podspeccs with canary version", async () => { - jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); - const specs = { - "./Test.podspec": specWithVersion("0.0.1"), - "./Test2.podspec": specWithVersion("0.0.1"), - }; - jest - .spyOn(utilities, "getPodspecContents") - .mockImplementation((path) => specs[path]); - const mock = jest - .spyOn(utilities, "writePodspecContents") - .mockImplementation((path, contents) => { - specs[path] = contents; - }); - - const newVersion = await multiHooks.canary.promise({ - bump: "minor" as Auto.SEMVER, - canaryIdentifier: "canary.1.1.1", - }); - - expect(newVersion).toBe("0.1.0-canary.1.1.1"); - expect(exec).toBeCalledTimes(6); - expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); - expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test2.podspec"]); - expect(mock).toBeCalledTimes(4); - expect(mock).toHaveBeenCalledWith( - "./Test.podspec", - specWithVersion( - "0.1.0-canary.1.1.1", - "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" - ) - ); - expect(mock).toHaveBeenCalledWith( - "./Test2.podspec", - specWithVersion( - "0.1.0-canary.1.1.1", - "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" - ) - ); - }); - test("should tag with canary version with no PR number", async () => { - let podSpec = specWithVersion("0.0.1"); - jest - .spyOn(utilities, "getPodspecContents") - .mockImplementation(() => podSpec); - const mock = jest - .spyOn(utilities, "writePodspecContents") - .mockImplementation((path, contents) => { - podSpec = contents; - }); - - const newVersion = await hooks.canary.promise({ - bump: "minor" as Auto.SEMVER, - canaryIdentifier: "canary.1.1.1", - }); - - expect(newVersion).toBe("0.1.0-canary.1.1.1"); - expect(exec).toBeCalledTimes(3); - expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); - - expect(mock).toHaveBeenLastCalledWith( - expect.any(String), - specWithVersion( - "0.1.0-canary.1.1.1", - "{ :git => 'https://github.com/intuit/auto.git', :commit => 'undefined' }" - ) - ); - }); - }); - - describe("next hook", () => { - test("should return prerelease versions on dryrun", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const versions = await hooks.next.promise(["0.0.1", "0.0.2"], { - bump: Auto.SEMVER.minor, - dryRun: true, - commits: [], - fullReleaseNotes: "", - releaseNotes: "", - }); - expect(versions).toStrictEqual(["0.0.1", "0.0.2", "0.1.0-next.0"]); - }); - test("should tag with next version", async () => { - jest.spyOn(Auto, "getCurrentBranch").mockReturnValue("next"); - const specs = { - "./Test.podspec": specWithVersion("0.0.1"), - "./Test2.podspec": specWithVersion("0.0.1"), - }; - jest - .spyOn(utilities, "getPodspecContents") - .mockImplementation((path) => specs[path]); - const mock = jest - .spyOn(utilities, "writePodspecContents") - .mockImplementation((path, contents) => { - specs[path] = contents; - }); - - const versions = await multiHooks.next.promise([], { - bump: Auto.SEMVER.major, - dryRun: false, - commits: [], - fullReleaseNotes: "", - releaseNotes: "", - }); - - expect(versions).toContain("1.0.0-next.0"); - expect(exec).toBeCalledTimes(6); - expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); - - expect(mock).toBeCalledTimes(2); - expect(mock).toHaveBeenCalledWith( - "./Test.podspec", - specWithVersion("1.0.0-next.0") - ); - expect(mock).toHaveBeenCalledWith( - "./Test2.podspec", - specWithVersion("1.0.0-next.0") - ); - }); - test("should tag with next version for multiple podspecs", async () => { - jest.spyOn(Auto, "getCurrentBranch").mockReturnValue("next"); - let podSpec = specWithVersion("0.0.1"); - jest - .spyOn(utilities, "getPodspecContents") - .mockImplementation(() => podSpec); - const mock = jest - .spyOn(utilities, "writePodspecContents") - .mockImplementation((path, contents) => { - podSpec = contents; - }); - - const versions = await hooks.next.promise([], { - bump: Auto.SEMVER.major, - dryRun: false, - commits: [], - fullReleaseNotes: "", - releaseNotes: "", - }); - - expect(versions).toContain("1.0.0-next.0"); - expect(exec).toBeCalledTimes(4); - expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); - - expect(mock).toHaveBeenLastCalledWith( - expect.any(String), - specWithVersion("1.0.0-next.0") - ); - }); - }); - - describe("publish hook", () => { - test("should push to trunk if no specsRepo in options", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const plugin = new CocoapodsPlugin(options); - const hook = makeHooks(); - plugin.apply({ - hooks: hook, - logger: dummyLog(), - prefixRelease, - } as Auto.Auto); - - await hook.publish.promise({ bump: Auto.SEMVER.patch }); - - expect(exec).toBeCalledTimes(2); - expect(exec).lastCalledWith("pod", ["trunk", "push", "./Test.podspec"]); - }); - test("should push multiple podspecs to trunk if no specsRepo in options", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const plugin = new CocoapodsPlugin(multiOptions); - const hook = makeHooks(); - plugin.apply({ - hooks: hook, - logger: dummyLog(), - prefixRelease, - } as Auto.Auto); - - await hook.publish.promise({ bump: Auto.SEMVER.patch }); - - expect(exec).toBeCalledTimes(3); - expect(exec).toHaveBeenCalledWith("pod", [ - "trunk", - "push", - "./Test.podspec", - ]); - expect(exec).toHaveBeenCalledWith("pod", [ - "trunk", - "push", - "./Test2.podspec", - ]); - }); - - test("should push with different pod command if in options", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const plugin = new CocoapodsPlugin({ ...options, podCommand: "notpod" }); - const hook = makeHooks(); - plugin.apply({ - hooks: hook, - logger: dummyLog(), - prefixRelease, - } as Auto.Auto); - - await hook.publish.promise({ bump: Auto.SEMVER.patch }); - - expect(exec).toBeCalledTimes(2); - expect(exec).lastCalledWith("notpod", [ - "trunk", - "push", - "./Test.podspec", - ]); - }); - - test("should push with different pod command with spaces if in options", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const plugin = new CocoapodsPlugin({ - ...options, - podCommand: "bundle exec pod", - }); - const hook = makeHooks(); - plugin.apply({ - hooks: hook, - logger: dummyLog(), - prefixRelease, - } as Auto.Auto); - - await hook.publish.promise({ bump: Auto.SEMVER.patch }); - - expect(exec).toBeCalledTimes(2); - expect(exec).lastCalledWith("bundle", [ - "exec", - "pod", - "trunk", - "push", - "./Test.podspec", - ]); - }); - - test("should push to trunk if no specsRepo in options with flags", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const logger = dummyLog(); - logger.logLevel = "verbose"; - - const plugin = new CocoapodsPlugin({ - ...options, - flags: ["--sources", "someOtherSpecsRepo"], - }); - const hook = makeHooks(); - plugin.apply({ - hooks: hook, - logger, - prefixRelease, - } as Auto.Auto); - - await hook.publish.promise({ bump: Auto.SEMVER.patch }); - - expect(exec).toBeCalledTimes(2); - expect(exec).lastCalledWith("pod", [ - "trunk", - "push", - "--sources", - "someOtherSpecsRepo", - "./Test.podspec", - "--verbose", - ]); - }); - - test("should push to specs repo if specsRepo in options", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const logger = dummyLog(); - logger.logLevel = "quiet"; - - const plugin = new CocoapodsPlugin({ - ...options, - specsRepo: "someSpecsRepo", - }); - const hook = makeHooks(); - plugin.apply({ - hooks: hook, - logger, - prefixRelease, - } as Auto.Auto); - - await hook.publish.promise({ bump: Auto.SEMVER.patch }); - - expect(exec).toBeCalledTimes(5); - expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); - expect(exec).toHaveBeenNthCalledWith(3, "pod", [ - "repo", - "add", - "autoPublishRepo", - "someSpecsRepo", - "--silent", - ]); - expect(exec).toHaveBeenNthCalledWith(4, "pod", [ - "repo", - "push", - "autoPublishRepo", - "./Test.podspec", - "--silent", - ]); - expect(exec).toHaveBeenNthCalledWith(5, "pod", [ - "repo", - "remove", - "autoPublishRepo", - "--silent", - ]); - }); - test("should push multiple podspecs to specs repo if specsRepo in options", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const logger = dummyLog(); - logger.logLevel = "quiet"; - - const plugin = new CocoapodsPlugin({ - ...multiOptions, - specsRepo: "someSpecsRepo", - }); - const hook = makeHooks(); - plugin.apply({ - hooks: hook, - logger, - prefixRelease, - } as Auto.Auto); - - await hook.publish.promise({ bump: Auto.SEMVER.patch }); - - expect(exec).toBeCalledTimes(6); - expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); - expect(exec).toHaveBeenNthCalledWith(3, "pod", [ - "repo", - "add", - "autoPublishRepo", - "someSpecsRepo", - "--silent", - ]); - expect(exec).toHaveBeenNthCalledWith(4, "pod", [ - "repo", - "push", - "autoPublishRepo", - "./Test.podspec", - "--silent", - ]); - expect(exec).toHaveBeenNthCalledWith(5, "pod", [ - "repo", - "push", - "autoPublishRepo", - "./Test2.podspec", - "--silent", - ]); - expect(exec).toHaveBeenNthCalledWith(6, "pod", [ - "repo", - "remove", - "autoPublishRepo", - "--silent", - ]); - }); - - test("should push to specs repo if specsRepo in options with flags", async () => { - mockPodspec(specWithVersion("0.0.1")); - - const logger = dummyLog(); - logger.logLevel = "veryVerbose"; - - const plugin = new CocoapodsPlugin({ - ...options, - specsRepo: "someSpecsRepo", - flags: ["--sources", "someOtherSpecsRepo"], - }); - const hook = makeHooks(); - plugin.apply({ - hooks: hook, - logger, - prefixRelease, - } as Auto.Auto); - - await hook.publish.promise({ bump: Auto.SEMVER.patch }); - - expect(exec).toBeCalledTimes(5); - expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); - expect(exec).toHaveBeenNthCalledWith(3, "pod", [ - "repo", - "add", - "autoPublishRepo", - "someSpecsRepo", - "--verbose", - ]); - expect(exec).toHaveBeenNthCalledWith(4, "pod", [ - "repo", - "push", - "--sources", - "someOtherSpecsRepo", - "autoPublishRepo", - "./Test.podspec", - "--verbose", - ]); - expect(exec).toHaveBeenNthCalledWith(5, "pod", [ - "repo", - "remove", - "autoPublishRepo", - "--verbose", - ]); - }); - - test("should delete autoPublishRepo if it exists and push to specs repo if specsRepo in options", async () => { - mockPodspec(specWithVersion("0.0.1")); - - exec = jest.fn().mockImplementation((...args) => { - if (args[1]?.[1] === "list") { - return ` -autoPublishRepo -- Type: git (master) -- URL: someSpecsRepo -- Path: /Users/someUser/.cocoapods/repos/autoPublishRepo - -master -- Type: git (master) -- URL: https://github.com/CocoaPods/Specs.git -- Path: /Users/someUser/.cocoapods/repos/master - -trunk -- Type: CDN -- URL: https://cdn.cocoapods.org/ -- Path: /Users/someUser/.cocoapods/repos/trunk - `; - } - }); - - const plugin = new CocoapodsPlugin({ - ...options, - specsRepo: "someSpecsRepo", - }); - const hook = makeHooks(); - plugin.apply({ - hooks: hook, - logger: dummyLog(), - prefixRelease, - } as Auto.Auto); - - await hook.publish.promise({ bump: Auto.SEMVER.patch }); - - expect(exec).toBeCalledTimes(6); - expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); - expect(exec).toHaveBeenNthCalledWith(3, "pod", [ - "repo", - "remove", - "autoPublishRepo", - ]); - expect(exec).toHaveBeenNthCalledWith(4, "pod", [ - "repo", - "add", - "autoPublishRepo", - "someSpecsRepo", - ]); - expect(exec).toHaveBeenNthCalledWith(5, "pod", [ - "repo", - "push", - "autoPublishRepo", - "./Test.podspec", - ]); - expect(exec).toHaveBeenNthCalledWith(6, "pod", [ - "repo", - "remove", - "autoPublishRepo", - ]); - }); - }); -}); diff --git a/plugins/fastlane/__tests__/fastlane.test.ts b/plugins/fastlane/__tests__/fastlane.test.ts new file mode 100644 index 000000000..c173542f0 --- /dev/null +++ b/plugins/fastlane/__tests__/fastlane.test.ts @@ -0,0 +1,1035 @@ +// 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 * as utilities from "../src/utilities"; +// import FastlanePlugin, { +// ICocoapodsPluginOptions, +// getpListContents, +// getVersion, +// } from "../src"; + +// const specWithVersion = ( +// version: string, +// source = "{ :git => 'https://github.com/intuit/auto.git', :tag => s.version.to_s }" +// ) => ` +// Pod:: Spec.new do | s | +// s.name = 'Test' +// s.version = '${version}' +// s.summary = 'A short description of Test.' + +// # This description is used to generate tags and improve search results. +// # * Think: What does it do? Why did you write it ? What is the focus ? +// # * Try to keep it short, snappy and to the point. +// # * Write the description between the DESC delimiters below. +// # * Finally, don't worry about the indent, CocoaPods strips it! + +// s.description = << -DESC +// TODO: Add long description of the pod here. +// DESC + +// s.homepage = 'https://github.com/intuit/auto' +// # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' +// s.license = { : type => 'MIT', : file => 'LICENSE' } +// s.author = { 'hborawski' => 'harris_borawski@intuit.com' } +// s.source = ${source} + +// s.ios.deployment_target = '11.0' + +// s.source_files = 'Test/Classes/**/*' + + +// # s.public_header_files = 'Pod/Classes/**/*.h' +// # s.frameworks = 'UIKit', 'MapKit' +// # s.dependency 'Alamofire' +// end +// `; + +// const mockPodspec = (contents: string) => { +// return jest.spyOn(utilities, "getPodspecContents").mockReturnValue(contents); +// }; + +// interface FakePodspec { +// path: string; +// contents: string; +// } +// const mockPodspecs = (contents: FakePodspec[]) => { +// return jest +// .spyOn(utilities, "getPodspecContents") +// .mockImplementation((path) => { +// return contents.find((podspec) => podspec.path === path).contents; +// }); +// }; + +// let exec = jest.fn(); +// // @ts-ignore +// jest.mock("../../../packages/core/dist/utils/exec-promise", () => (...args) => +// exec(...args) +// ); +// const logger = dummyLog(); + +// describe("Cocoapods Plugin", () => { +// let hooks: Auto.IAutoHooks; +// let multiHooks: Auto.IAutoHooks; +// const prefixRelease: (a: string) => string = (version: string) => { +// return `v${version}`; +// }; + +// const options: ICocoapodsPluginOptions = { +// podspecPath: "./Test.podspec", +// }; + +// const multiOptions: ICocoapodsPluginOptions = { +// podspecPath: ["./Test.podspec", "./Test2.podspec"], +// }; + +// beforeEach(() => { +// jest.resetAllMocks(); +// exec.mockClear(); +// const plugin = new CocoapodsPlugin(options); +// const multiPlugin = new CocoapodsPlugin(multiOptions); +// hooks = makeHooks(); +// multiHooks = makeHooks(); +// const apply = (p: CocoapodsPlugin, h: Auto.IAutoHooks) => { +// p.apply(({ +// hooks: h, +// logger: logger, +// prefixRelease, +// git: { +// getLastTagNotInBaseBranch: async () => undefined, +// getLatestRelease: async () => "0.0.1", +// getPullRequest: async () => ({ +// data: { +// head: { +// repo: { +// clone_url: "https://github.com/intuit-fork/auto.git", +// }, +// }, +// }, +// }), +// }, +// remote: "https://github.com/intuit/auto.git", +// getCurrentVersion: async () => "0.0.1", +// } as unknown) as Auto.Auto); +// }; + +// apply(plugin, hooks); +// apply(multiPlugin, multiHooks); +// }); + +// describe("getParsedPodspecContents", () => { +// test("should return null if contents cant be parsed with regex", () => { +// mockPodspec("bad podspec"); + +// expect(getParsedPodspecContents("./Test.podspec")).toBeNull(); +// }); +// test("should return parsed contents", () => { +// mockPodspec(specWithVersion("0.0.1")); +// const contents = getParsedPodspecContents("./Test.podspec"); +// expect(contents).toHaveProperty("groups", { version: "0.0.1" }); +// }); +// }); +// describe("getVersion", () => { +// test("should throw error if parsed podspec is returned as null", () => { +// mockPodspec("bad podspec"); + +// expect(() => getVersion("./Test.podspec")).toThrow(); +// }); +// test("should return version", () => { +// mockPodspec(specWithVersion("0.0.1")); + +// expect(getVersion("./Test.podspec")).toBe("0.0.1"); +// }); +// test("should return canary version", () => { +// mockPodspec(specWithVersion("0.0.1-canary.1.0.0")); + +// expect(getVersion("./Test.podspec")).toBe("0.0.1-canary.1.0.0"); +// }); +// }); +// describe("getSourceInfo", () => { +// test("should throw error if source line cant be found", () => { +// mockPodspec(specWithVersion("0.0.1", "no source")); + +// expect(() => getSourceInfo("./Test.podspec")).toThrow(); +// }); +// test("should retrieve source info", () => { +// mockPodspec(specWithVersion("0.0.1")); + +// expect(getSourceInfo("./Test.podspec")).toBe( +// "{ :git => 'https://github.com/intuit/auto.git', :tag => s.version.to_s }" +// ); +// }); +// }); +// describe("updatePodspecVersion", () => { +// test("should throw error if there is an error writing file", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// jest +// .spyOn(utilities, "writePodspecContents") +// .mockImplementationOnce(() => { +// throw new Error("Filesystem Error"); +// }); + +// expect( +// updatePodspecVersion.bind(null, "./Test.podspec", "0.0.2") +// ).toThrowError("Error updating version in podspec: ./Test.podspec"); +// }); +// test("should successfully write new version", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const mock = jest.spyOn(utilities, "writePodspecContents"); + +// await updatePodspecVersion("./Test.podspec", "0.0.2"); +// expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); +// }); +// }); +// describe("updateSourceLocation", () => { +// test("should throw error if there is an error writing file", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// exec.mockReturnValue("commithash"); + +// jest +// .spyOn(utilities, "writePodspecContents") +// .mockImplementationOnce(() => { +// throw new Error("Filesystem Error"); +// }); + +// await expect( +// updateSourceLocation( +// "./Test.podspec", +// "https://github.com/somefork/auto.git" +// ) +// ).rejects.toThrowError( +// "Error updating source location in podspec: ./Test.podspec" +// ); +// }); +// test("should successfully write new source location", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const mock = jest.spyOn(utilities, "writePodspecContents"); + +// exec.mockReturnValue(Promise.resolve("commithash")); + +// await updateSourceLocation( +// "./Test.podspec", +// "https://github.com/somefork/auto.git" +// ); +// expect(mock).lastCalledWith( +// expect.any(String), +// specWithVersion( +// "0.0.1", +// "{ :git => 'https://github.com/somefork/auto.git', :commit => 'commithash' }" +// ) +// ); +// }); +// }); +// describe("validateConfig hook", () => { +// test("should validate options", async () => { +// expect( +// ((await hooks.validateConfig.promise("cocoapods", {})) || []) +// ).toHaveLength(1); +// expect( +// ((await hooks.validateConfig.promise("cocoapods", options)) || []) + +// ).toHaveLength(0); +// expect( +// ((await hooks.validateConfig.promise("cocoapods", multiOptions)) || []) + +// ).toHaveLength(0); +// }); +// }); +// describe("modifyConfig hook", () => { +// test("should set noVersionPrefix to true", async () => { +// const config = {}; +// expect(await hooks.modifyConfig.promise(config as any)).toStrictEqual({ +// noVersionPrefix: true, +// }); +// }); +// }); +// describe("getPreviousVersion hook", () => { +// test("should get previous version from podspec", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// expect(await hooks.getPreviousVersion.promise()).toBe("v0.0.1"); +// }); + +// test("should throw if no version found", async () => { +// mockPodspec(specWithVersion("")); + +// await expect(hooks.getPreviousVersion.promise()).rejects.toThrowError( +// "Version could not be found in podspec: ./Test.podspec" +// ); +// }); + +// test("should get version if multiple podspecs", async () => { +// mockPodspecs([ +// { +// path: "./Test.podspec", +// contents: specWithVersion("0.0.1"), +// }, +// { +// path: "./Test2.podspec", +// contents: specWithVersion("0.0.1"), +// }, +// ]); +// expect(await multiHooks.getPreviousVersion.promise()).toBe("v0.0.1"); +// }); +// }); +// describe("version hook", () => { +// test("should do nothing on dryRun", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const mockLog = jest.spyOn(logger.log, "info"); + +// await hooks.version.promise({ bump: Auto.SEMVER.patch, dryRun: true }); + +// expect(exec).toHaveBeenCalledTimes(0); +// expect(mockLog).toHaveBeenCalledTimes(1); +// }); +// test("should not use logger on quiet dryRun", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const mockLog = jest.spyOn(logger.log, "info"); +// const mockConsole = jest.spyOn(console, "log"); + +// await hooks.version.promise({ +// bump: Auto.SEMVER.patch, +// dryRun: true, +// quiet: true, +// }); + +// expect(exec).toHaveBeenCalledTimes(0); +// expect(mockLog).toHaveBeenCalledTimes(0); +// expect(mockConsole).toHaveBeenCalledTimes(1); +// }); +// test("should version release - patch version", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const mock = jest.spyOn(utilities, "writePodspecContents"); + +// await hooks.version.promise({ bump: Auto.SEMVER.patch }); + +// expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); +// }); +// test("should version release - minor version", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const mock = jest.spyOn(utilities, "writePodspecContents"); + +// await hooks.version.promise({ bump: Auto.SEMVER.minor }); + +// expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.1.0")); +// }); +// test("should version release - major version", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const mock = jest.spyOn(utilities, "writePodspecContents"); + +// await hooks.version.promise({ bump: Auto.SEMVER.major }); + +// expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); +// }); +// test("should release version multiple podspecs - patch", async () => { +// mockPodspecs([ +// { +// path: "./Test.podspec", +// contents: specWithVersion("0.0.1"), +// }, +// { +// path: "./Test2.podspec", +// contents: specWithVersion("0.0.1"), +// }, +// ]); +// const mock = jest.spyOn(utilities, "writePodspecContents"); +// await multiHooks.version.promise({ bump: Auto.SEMVER.patch }); +// expect(mock).toHaveBeenCalledTimes(2); +// expect(mock).toHaveBeenNthCalledWith( +// 1, +// "./Test.podspec", +// specWithVersion("0.0.2") +// ); +// expect(mock).toHaveBeenNthCalledWith( +// 2, +// "./Test2.podspec", +// specWithVersion("0.0.2") +// ); +// }); +// test("should release version multiple podspecs - minor", async () => { +// mockPodspecs([ +// { +// path: "./Test.podspec", +// contents: specWithVersion("0.0.1"), +// }, +// { +// path: "./Test2.podspec", +// contents: specWithVersion("0.0.1"), +// }, +// ]); +// const mock = jest.spyOn(utilities, "writePodspecContents"); +// await multiHooks.version.promise({ bump: Auto.SEMVER.minor }); +// expect(mock).toHaveBeenCalledTimes(2); +// expect(mock).toHaveBeenNthCalledWith( +// 1, +// "./Test.podspec", +// specWithVersion("0.1.0") +// ); +// expect(mock).toHaveBeenNthCalledWith( +// 2, +// "./Test2.podspec", +// specWithVersion("0.1.0") +// ); +// }); +// test("should release version multiple podspecs - major", async () => { +// mockPodspecs([ +// { +// path: "./Test.podspec", +// contents: specWithVersion("0.0.1"), +// }, +// { +// path: "./Test2.podspec", +// contents: specWithVersion("0.0.1"), +// }, +// ]); +// const mock = jest.spyOn(utilities, "writePodspecContents"); +// await multiHooks.version.promise({ bump: Auto.SEMVER.major }); +// expect(mock).toHaveBeenCalledTimes(2); +// expect(mock).toHaveBeenNthCalledWith( +// 1, +// "./Test.podspec", +// specWithVersion("1.0.0") +// ); +// expect(mock).toHaveBeenNthCalledWith( +// 2, +// "./Test2.podspec", +// specWithVersion("1.0.0") +// ); +// }); +// test("should throw if there is an error writing new version", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const mock = jest +// .spyOn(utilities, "writePodspecContents") +// .mockImplementation((path, contents) => { +// if (contents.includes("1.0.0")) { +// throw new Error("Filesystem Error"); +// } +// }); + +// await expect( +// hooks.version.promise({ bump: Auto.SEMVER.major }) +// ).rejects.toThrowError( +// "Error updating version in podspec: ./Test.podspec" +// ); + +// expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); +// }); +// }); + +// describe("beforeShipit hook", () => { +// test("should call pod lib lint with dryRun flag", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const plugin = new CocoapodsPlugin(options); +// const hook = makeHooks(); + +// plugin.apply({ +// hooks: hook, +// logger: dummyLog(), +// prefixRelease, +// } as Auto.Auto); + +// await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); + +// expect(exec).toBeCalledTimes(1); +// expect(exec).lastCalledWith("pod", ["lib", "lint", "./Test.podspec"]); +// }); +// test("should call pod lib lint for each podspec with dryRun flag", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const plugin = new CocoapodsPlugin(multiOptions); +// const hook = makeHooks(); + +// plugin.apply({ +// hooks: hook, +// logger: dummyLog(), +// prefixRelease, +// } as Auto.Auto); + +// await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); + +// expect(exec).toBeCalledTimes(2); +// expect(exec).toHaveBeenNthCalledWith(1, "pod", [ +// "lib", +// "lint", +// "./Test.podspec", +// ]); +// expect(exec).toHaveBeenNthCalledWith(2, "pod", [ +// "lib", +// "lint", +// "./Test2.podspec", +// ]); +// }); +// test("should call pod lib lint with options with dryRun flag", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const plugin = new CocoapodsPlugin({ +// ...options, +// flags: ["--flag"], +// podCommand: "notpod", +// }); +// const hook = makeHooks(); + +// plugin.apply({ +// hooks: hook, +// logger: dummyLog(), +// prefixRelease, +// } as Auto.Auto); + +// await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); + +// expect(exec).toBeCalledTimes(1); +// expect(exec).lastCalledWith("notpod", [ +// "lib", +// "lint", +// "--flag", +// "./Test.podspec", +// ]); +// }); +// }); + +// describe("canary hook", () => { +// test("should do nothing on dryRun", async () => { +// mockPodspec(specWithVersion("0.0.1")); +// jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); + +// const mockLog = jest.spyOn(logger.log, "info"); + +// await hooks.canary.promise({ +// bump: Auto.SEMVER.patch, +// canaryIdentifier: "canary.1.0", +// dryRun: true, +// }); + +// expect(exec).toHaveBeenCalledTimes(0); +// expect(mockLog).toHaveBeenCalledTimes(1); +// }); +// test("should not use logger on quiet dryRun", async () => { +// mockPodspec(specWithVersion("0.0.1")); +// jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); + +// const mockLog = jest.spyOn(logger.log, "info"); +// const mockConsole = jest.spyOn(console, "log"); + +// await hooks.canary.promise({ +// bump: Auto.SEMVER.patch, +// canaryIdentifier: "canary.1.0", +// dryRun: true, +// quiet: true, +// }); + +// expect(exec).toHaveBeenCalledTimes(0); +// expect(mockLog).toHaveBeenCalledTimes(0); +// expect(mockConsole).toHaveBeenCalledTimes(1); +// }); +// test("should tag with canary version", async () => { +// jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); +// let podSpec = specWithVersion("0.0.1"); +// jest +// .spyOn(utilities, "getPodspecContents") +// .mockImplementation(() => podSpec); +// const mock = jest +// .spyOn(utilities, "writePodspecContents") +// .mockImplementation((path, contents) => { +// podSpec = contents; +// }); + +// const newVersion = await hooks.canary.promise({ +// bump: "minor" as Auto.SEMVER, +// canaryIdentifier: "canary.1.1.1", +// }); + +// expect(newVersion).toBe("0.1.0-canary.1.1.1"); +// expect(exec).toBeCalledTimes(3); +// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); + +// expect(mock).toHaveBeenLastCalledWith( +// expect.any(String), +// specWithVersion( +// "0.1.0-canary.1.1.1", +// "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" +// ) +// ); +// }); +// test("should tag multiple podspeccs with canary version", async () => { +// jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); +// const specs = { +// "./Test.podspec": specWithVersion("0.0.1"), +// "./Test2.podspec": specWithVersion("0.0.1"), +// }; +// jest +// .spyOn(utilities, "getPodspecContents") +// .mockImplementation((path) => specs[path]); +// const mock = jest +// .spyOn(utilities, "writePodspecContents") +// .mockImplementation((path, contents) => { +// specs[path] = contents; +// }); + +// const newVersion = await multiHooks.canary.promise({ +// bump: "minor" as Auto.SEMVER, +// canaryIdentifier: "canary.1.1.1", +// }); + +// expect(newVersion).toBe("0.1.0-canary.1.1.1"); +// expect(exec).toBeCalledTimes(6); +// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); +// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test2.podspec"]); +// expect(mock).toBeCalledTimes(4); +// expect(mock).toHaveBeenCalledWith( +// "./Test.podspec", +// specWithVersion( +// "0.1.0-canary.1.1.1", +// "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" +// ) +// ); +// expect(mock).toHaveBeenCalledWith( +// "./Test2.podspec", +// specWithVersion( +// "0.1.0-canary.1.1.1", +// "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" +// ) +// ); +// }); +// test("should tag with canary version with no PR number", async () => { +// let podSpec = specWithVersion("0.0.1"); +// jest +// .spyOn(utilities, "getPodspecContents") +// .mockImplementation(() => podSpec); +// const mock = jest +// .spyOn(utilities, "writePodspecContents") +// .mockImplementation((path, contents) => { +// podSpec = contents; +// }); + +// const newVersion = await hooks.canary.promise({ +// bump: "minor" as Auto.SEMVER, +// canaryIdentifier: "canary.1.1.1", +// }); + +// expect(newVersion).toBe("0.1.0-canary.1.1.1"); +// expect(exec).toBeCalledTimes(3); +// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); + +// expect(mock).toHaveBeenLastCalledWith( +// expect.any(String), +// specWithVersion( +// "0.1.0-canary.1.1.1", +// "{ :git => 'https://github.com/intuit/auto.git', :commit => 'undefined' }" +// ) +// ); +// }); +// }); + +// describe("next hook", () => { +// test("should return prerelease versions on dryrun", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const versions = await hooks.next.promise(["0.0.1", "0.0.2"], { +// bump: Auto.SEMVER.minor, +// dryRun: true, +// commits: [], +// fullReleaseNotes: "", +// releaseNotes: "", +// }); +// expect(versions).toStrictEqual(["0.0.1", "0.0.2", "0.1.0-next.0"]); +// }); +// test("should tag with next version", async () => { +// jest.spyOn(Auto, "getCurrentBranch").mockReturnValue("next"); +// const specs = { +// "./Test.podspec": specWithVersion("0.0.1"), +// "./Test2.podspec": specWithVersion("0.0.1"), +// }; +// jest +// .spyOn(utilities, "getPodspecContents") +// .mockImplementation((path) => specs[path]); +// const mock = jest +// .spyOn(utilities, "writePodspecContents") +// .mockImplementation((path, contents) => { +// specs[path] = contents; +// }); + +// const versions = await multiHooks.next.promise([], { +// bump: Auto.SEMVER.major, +// dryRun: false, +// commits: [], +// fullReleaseNotes: "", +// releaseNotes: "", +// }); + +// expect(versions).toContain("1.0.0-next.0"); +// expect(exec).toBeCalledTimes(6); +// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); + +// expect(mock).toBeCalledTimes(2); +// expect(mock).toHaveBeenCalledWith( +// "./Test.podspec", +// specWithVersion("1.0.0-next.0") +// ); +// expect(mock).toHaveBeenCalledWith( +// "./Test2.podspec", +// specWithVersion("1.0.0-next.0") +// ); +// }); +// test("should tag with next version for multiple podspecs", async () => { +// jest.spyOn(Auto, "getCurrentBranch").mockReturnValue("next"); +// let podSpec = specWithVersion("0.0.1"); +// jest +// .spyOn(utilities, "getPodspecContents") +// .mockImplementation(() => podSpec); +// const mock = jest +// .spyOn(utilities, "writePodspecContents") +// .mockImplementation((path, contents) => { +// podSpec = contents; +// }); + +// const versions = await hooks.next.promise([], { +// bump: Auto.SEMVER.major, +// dryRun: false, +// commits: [], +// fullReleaseNotes: "", +// releaseNotes: "", +// }); + +// expect(versions).toContain("1.0.0-next.0"); +// expect(exec).toBeCalledTimes(4); +// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); + +// expect(mock).toHaveBeenLastCalledWith( +// expect.any(String), +// specWithVersion("1.0.0-next.0") +// ); +// }); +// }); + +// describe("publish hook", () => { +// test("should push to trunk if no specsRepo in options", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const plugin = new CocoapodsPlugin(options); +// const hook = makeHooks(); +// plugin.apply({ +// hooks: hook, +// logger: dummyLog(), +// prefixRelease, +// } as Auto.Auto); + +// await hook.publish.promise({ bump: Auto.SEMVER.patch }); + +// expect(exec).toBeCalledTimes(2); +// expect(exec).lastCalledWith("pod", ["trunk", "push", "./Test.podspec"]); +// }); +// test("should push multiple podspecs to trunk if no specsRepo in options", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const plugin = new CocoapodsPlugin(multiOptions); +// const hook = makeHooks(); +// plugin.apply({ +// hooks: hook, +// logger: dummyLog(), +// prefixRelease, +// } as Auto.Auto); + +// await hook.publish.promise({ bump: Auto.SEMVER.patch }); + +// expect(exec).toBeCalledTimes(3); +// expect(exec).toHaveBeenCalledWith("pod", [ +// "trunk", +// "push", +// "./Test.podspec", +// ]); +// expect(exec).toHaveBeenCalledWith("pod", [ +// "trunk", +// "push", +// "./Test2.podspec", +// ]); +// }); + +// test("should push with different pod command if in options", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const plugin = new CocoapodsPlugin({ ...options, podCommand: "notpod" }); +// const hook = makeHooks(); +// plugin.apply({ +// hooks: hook, +// logger: dummyLog(), +// prefixRelease, +// } as Auto.Auto); + +// await hook.publish.promise({ bump: Auto.SEMVER.patch }); + +// expect(exec).toBeCalledTimes(2); +// expect(exec).lastCalledWith("notpod", [ +// "trunk", +// "push", +// "./Test.podspec", +// ]); +// }); + +// test("should push with different pod command with spaces if in options", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const plugin = new CocoapodsPlugin({ +// ...options, +// podCommand: "bundle exec pod", +// }); +// const hook = makeHooks(); +// plugin.apply({ +// hooks: hook, +// logger: dummyLog(), +// prefixRelease, +// } as Auto.Auto); + +// await hook.publish.promise({ bump: Auto.SEMVER.patch }); + +// expect(exec).toBeCalledTimes(2); +// expect(exec).lastCalledWith("bundle", [ +// "exec", +// "pod", +// "trunk", +// "push", +// "./Test.podspec", +// ]); +// }); + +// test("should push to trunk if no specsRepo in options with flags", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const logger = dummyLog(); +// logger.logLevel = "verbose"; + +// const plugin = new CocoapodsPlugin({ +// ...options, +// flags: ["--sources", "someOtherSpecsRepo"], +// }); +// const hook = makeHooks(); +// plugin.apply({ +// hooks: hook, +// logger, +// prefixRelease, +// } as Auto.Auto); + +// await hook.publish.promise({ bump: Auto.SEMVER.patch }); + +// expect(exec).toBeCalledTimes(2); +// expect(exec).lastCalledWith("pod", [ +// "trunk", +// "push", +// "--sources", +// "someOtherSpecsRepo", +// "./Test.podspec", +// "--verbose", +// ]); +// }); + +// test("should push to specs repo if specsRepo in options", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const logger = dummyLog(); +// logger.logLevel = "quiet"; + +// const plugin = new CocoapodsPlugin({ +// ...options, +// specsRepo: "someSpecsRepo", +// }); +// const hook = makeHooks(); +// plugin.apply({ +// hooks: hook, +// logger, +// prefixRelease, +// } as Auto.Auto); + +// await hook.publish.promise({ bump: Auto.SEMVER.patch }); + +// expect(exec).toBeCalledTimes(5); +// expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); +// expect(exec).toHaveBeenNthCalledWith(3, "pod", [ +// "repo", +// "add", +// "autoPublishRepo", +// "someSpecsRepo", +// "--silent", +// ]); +// expect(exec).toHaveBeenNthCalledWith(4, "pod", [ +// "repo", +// "push", +// "autoPublishRepo", +// "./Test.podspec", +// "--silent", +// ]); +// expect(exec).toHaveBeenNthCalledWith(5, "pod", [ +// "repo", +// "remove", +// "autoPublishRepo", +// "--silent", +// ]); +// }); +// test("should push multiple podspecs to specs repo if specsRepo in options", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const logger = dummyLog(); +// logger.logLevel = "quiet"; + +// const plugin = new CocoapodsPlugin({ +// ...multiOptions, +// specsRepo: "someSpecsRepo", +// }); +// const hook = makeHooks(); +// plugin.apply({ +// hooks: hook, +// logger, +// prefixRelease, +// } as Auto.Auto); + +// await hook.publish.promise({ bump: Auto.SEMVER.patch }); + +// expect(exec).toBeCalledTimes(6); +// expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); +// expect(exec).toHaveBeenNthCalledWith(3, "pod", [ +// "repo", +// "add", +// "autoPublishRepo", +// "someSpecsRepo", +// "--silent", +// ]); +// expect(exec).toHaveBeenNthCalledWith(4, "pod", [ +// "repo", +// "push", +// "autoPublishRepo", +// "./Test.podspec", +// "--silent", +// ]); +// expect(exec).toHaveBeenNthCalledWith(5, "pod", [ +// "repo", +// "push", +// "autoPublishRepo", +// "./Test2.podspec", +// "--silent", +// ]); +// expect(exec).toHaveBeenNthCalledWith(6, "pod", [ +// "repo", +// "remove", +// "autoPublishRepo", +// "--silent", +// ]); +// }); + +// test("should push to specs repo if specsRepo in options with flags", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// const logger = dummyLog(); +// logger.logLevel = "veryVerbose"; + +// const plugin = new CocoapodsPlugin({ +// ...options, +// specsRepo: "someSpecsRepo", +// flags: ["--sources", "someOtherSpecsRepo"], +// }); +// const hook = makeHooks(); +// plugin.apply({ +// hooks: hook, +// logger, +// prefixRelease, +// } as Auto.Auto); + +// await hook.publish.promise({ bump: Auto.SEMVER.patch }); + +// expect(exec).toBeCalledTimes(5); +// expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); +// expect(exec).toHaveBeenNthCalledWith(3, "pod", [ +// "repo", +// "add", +// "autoPublishRepo", +// "someSpecsRepo", +// "--verbose", +// ]); +// expect(exec).toHaveBeenNthCalledWith(4, "pod", [ +// "repo", +// "push", +// "--sources", +// "someOtherSpecsRepo", +// "autoPublishRepo", +// "./Test.podspec", +// "--verbose", +// ]); +// expect(exec).toHaveBeenNthCalledWith(5, "pod", [ +// "repo", +// "remove", +// "autoPublishRepo", +// "--verbose", +// ]); +// }); + +// test("should delete autoPublishRepo if it exists and push to specs repo if specsRepo in options", async () => { +// mockPodspec(specWithVersion("0.0.1")); + +// exec = jest.fn().mockImplementation((...args) => { +// if (args[1]?.[1] === "list") { +// return ` +// autoPublishRepo +// - Type: git (master) +// - URL: someSpecsRepo +// - Path: /Users/someUser/.cocoapods/repos/autoPublishRepo + +// master +// - Type: git (master) +// - URL: https://github.com/CocoaPods/Specs.git +// - Path: /Users/someUser/.cocoapods/repos/master + +// trunk +// - Type: CDN +// - URL: https://cdn.cocoapods.org/ +// - Path: /Users/someUser/.cocoapods/repos/trunk +// `; +// } +// }); + +// const plugin = new CocoapodsPlugin({ +// ...options, +// specsRepo: "someSpecsRepo", +// }); +// const hook = makeHooks(); +// plugin.apply({ +// hooks: hook, +// logger: dummyLog(), +// prefixRelease, +// } as Auto.Auto); + +// await hook.publish.promise({ bump: Auto.SEMVER.patch }); + +// expect(exec).toBeCalledTimes(6); +// expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); +// expect(exec).toHaveBeenNthCalledWith(3, "pod", [ +// "repo", +// "remove", +// "autoPublishRepo", +// ]); +// expect(exec).toHaveBeenNthCalledWith(4, "pod", [ +// "repo", +// "add", +// "autoPublishRepo", +// "someSpecsRepo", +// ]); +// expect(exec).toHaveBeenNthCalledWith(5, "pod", [ +// "repo", +// "push", +// "autoPublishRepo", +// "./Test.podspec", +// ]); +// expect(exec).toHaveBeenNthCalledWith(6, "pod", [ +// "repo", +// "remove", +// "autoPublishRepo", +// ]); +// }); +// }); +// }); From 1401b8c6dbb42a5dc769ee3c2d49d3f1f6ea294e Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Fri, 15 Jul 2022 13:21:49 +0530 Subject: [PATCH 10/12] adding logging statement --- packages/core/src/semver.ts | 4 +- plugins/fastlane/README.md | 47 - plugins/fastlane/__tests__/fastlane.test.ts | 1035 ------------------- plugins/fastlane/package.json | 45 - plugins/fastlane/src/index.ts | 165 --- plugins/fastlane/src/utilities.ts | 12 - plugins/fastlane/tsconfig.json | 16 - 7 files changed, 3 insertions(+), 1321 deletions(-) delete mode 100644 plugins/fastlane/README.md delete mode 100644 plugins/fastlane/__tests__/fastlane.test.ts delete mode 100644 plugins/fastlane/package.json delete mode 100644 plugins/fastlane/src/index.ts delete mode 100644 plugins/fastlane/src/utilities.ts delete mode 100644 plugins/fastlane/tsconfig.json diff --git a/packages/core/src/semver.ts b/packages/core/src/semver.ts index 2e0fea2e0..fbf7422c7 100644 --- a/packages/core/src/semver.ts +++ b/packages/core/src/semver.ts @@ -217,11 +217,13 @@ export function calculateSemVerBump( }); const lastMergedCommitLabels = prLabels[0] || []; + console.log("Last Merged Commit Labels: ", lastMergedCommitLabels) const releaseLabels = labelMap.get("release") || []; + console.log("Release Labels: ", releaseLabels) const skipRelease = onlyPublishWithReleaseLabel ? !lastMergedCommitLabels.some((label) => releaseLabels.includes(label)) : lastMergedCommitLabels.some((label) => skipReleaseLabels.includes(label)); - + console.log("skipRelease Value: ", skipRelease) if (skipRelease) { return SEMVER.noVersion; } diff --git a/plugins/fastlane/README.md b/plugins/fastlane/README.md deleted file mode 100644 index ec3f43d0a..000000000 --- a/plugins/fastlane/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Fastlane Plugin - -Use `auto` to version your [pList] using fastlane, and push to your repository! - -## Installation - -This plugin is not included with the `auto` CLI installed via NPM. To install: - -```bash -npm i --save-dev @auto-it/fastlane -# or -yarn add -D @auto-it/fastlane -``` - -> WARNING: You can only use one "package manager" at a time! -> Mixing them will lead to undesired results. - -## Usage - -```json -{ - "plugins": [ - [ - "fastlane", - { - // Required, the relative path to your pList file - "pListPath": "./Example/Info.plist", - // Optional, specify a different executable for `fastlane` - "fastlaneCommand": "bundle exec fastlane" - } - ] - // other plugins - ] -} -``` - -## Requirements - -### General - -- The machine running this plugin must have the [fastlane](https://fastlane.tools/) `fastlane` CLI installed already, or `fastlaneCommand` specified in your plugin configuration. -- Using the logging flags with Auto (`auto -v`, `auto -vv`, `auto -q`) will also add the verbose or silent flags to the fastlane commands. - -### Pushing to the Repository - - -### Pushing to a private repository \ No newline at end of file diff --git a/plugins/fastlane/__tests__/fastlane.test.ts b/plugins/fastlane/__tests__/fastlane.test.ts deleted file mode 100644 index c173542f0..000000000 --- a/plugins/fastlane/__tests__/fastlane.test.ts +++ /dev/null @@ -1,1035 +0,0 @@ -// 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 * as utilities from "../src/utilities"; -// import FastlanePlugin, { -// ICocoapodsPluginOptions, -// getpListContents, -// getVersion, -// } from "../src"; - -// const specWithVersion = ( -// version: string, -// source = "{ :git => 'https://github.com/intuit/auto.git', :tag => s.version.to_s }" -// ) => ` -// Pod:: Spec.new do | s | -// s.name = 'Test' -// s.version = '${version}' -// s.summary = 'A short description of Test.' - -// # This description is used to generate tags and improve search results. -// # * Think: What does it do? Why did you write it ? What is the focus ? -// # * Try to keep it short, snappy and to the point. -// # * Write the description between the DESC delimiters below. -// # * Finally, don't worry about the indent, CocoaPods strips it! - -// s.description = << -DESC -// TODO: Add long description of the pod here. -// DESC - -// s.homepage = 'https://github.com/intuit/auto' -// # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' -// s.license = { : type => 'MIT', : file => 'LICENSE' } -// s.author = { 'hborawski' => 'harris_borawski@intuit.com' } -// s.source = ${source} - -// s.ios.deployment_target = '11.0' - -// s.source_files = 'Test/Classes/**/*' - - -// # s.public_header_files = 'Pod/Classes/**/*.h' -// # s.frameworks = 'UIKit', 'MapKit' -// # s.dependency 'Alamofire' -// end -// `; - -// const mockPodspec = (contents: string) => { -// return jest.spyOn(utilities, "getPodspecContents").mockReturnValue(contents); -// }; - -// interface FakePodspec { -// path: string; -// contents: string; -// } -// const mockPodspecs = (contents: FakePodspec[]) => { -// return jest -// .spyOn(utilities, "getPodspecContents") -// .mockImplementation((path) => { -// return contents.find((podspec) => podspec.path === path).contents; -// }); -// }; - -// let exec = jest.fn(); -// // @ts-ignore -// jest.mock("../../../packages/core/dist/utils/exec-promise", () => (...args) => -// exec(...args) -// ); -// const logger = dummyLog(); - -// describe("Cocoapods Plugin", () => { -// let hooks: Auto.IAutoHooks; -// let multiHooks: Auto.IAutoHooks; -// const prefixRelease: (a: string) => string = (version: string) => { -// return `v${version}`; -// }; - -// const options: ICocoapodsPluginOptions = { -// podspecPath: "./Test.podspec", -// }; - -// const multiOptions: ICocoapodsPluginOptions = { -// podspecPath: ["./Test.podspec", "./Test2.podspec"], -// }; - -// beforeEach(() => { -// jest.resetAllMocks(); -// exec.mockClear(); -// const plugin = new CocoapodsPlugin(options); -// const multiPlugin = new CocoapodsPlugin(multiOptions); -// hooks = makeHooks(); -// multiHooks = makeHooks(); -// const apply = (p: CocoapodsPlugin, h: Auto.IAutoHooks) => { -// p.apply(({ -// hooks: h, -// logger: logger, -// prefixRelease, -// git: { -// getLastTagNotInBaseBranch: async () => undefined, -// getLatestRelease: async () => "0.0.1", -// getPullRequest: async () => ({ -// data: { -// head: { -// repo: { -// clone_url: "https://github.com/intuit-fork/auto.git", -// }, -// }, -// }, -// }), -// }, -// remote: "https://github.com/intuit/auto.git", -// getCurrentVersion: async () => "0.0.1", -// } as unknown) as Auto.Auto); -// }; - -// apply(plugin, hooks); -// apply(multiPlugin, multiHooks); -// }); - -// describe("getParsedPodspecContents", () => { -// test("should return null if contents cant be parsed with regex", () => { -// mockPodspec("bad podspec"); - -// expect(getParsedPodspecContents("./Test.podspec")).toBeNull(); -// }); -// test("should return parsed contents", () => { -// mockPodspec(specWithVersion("0.0.1")); -// const contents = getParsedPodspecContents("./Test.podspec"); -// expect(contents).toHaveProperty("groups", { version: "0.0.1" }); -// }); -// }); -// describe("getVersion", () => { -// test("should throw error if parsed podspec is returned as null", () => { -// mockPodspec("bad podspec"); - -// expect(() => getVersion("./Test.podspec")).toThrow(); -// }); -// test("should return version", () => { -// mockPodspec(specWithVersion("0.0.1")); - -// expect(getVersion("./Test.podspec")).toBe("0.0.1"); -// }); -// test("should return canary version", () => { -// mockPodspec(specWithVersion("0.0.1-canary.1.0.0")); - -// expect(getVersion("./Test.podspec")).toBe("0.0.1-canary.1.0.0"); -// }); -// }); -// describe("getSourceInfo", () => { -// test("should throw error if source line cant be found", () => { -// mockPodspec(specWithVersion("0.0.1", "no source")); - -// expect(() => getSourceInfo("./Test.podspec")).toThrow(); -// }); -// test("should retrieve source info", () => { -// mockPodspec(specWithVersion("0.0.1")); - -// expect(getSourceInfo("./Test.podspec")).toBe( -// "{ :git => 'https://github.com/intuit/auto.git', :tag => s.version.to_s }" -// ); -// }); -// }); -// describe("updatePodspecVersion", () => { -// test("should throw error if there is an error writing file", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// jest -// .spyOn(utilities, "writePodspecContents") -// .mockImplementationOnce(() => { -// throw new Error("Filesystem Error"); -// }); - -// expect( -// updatePodspecVersion.bind(null, "./Test.podspec", "0.0.2") -// ).toThrowError("Error updating version in podspec: ./Test.podspec"); -// }); -// test("should successfully write new version", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const mock = jest.spyOn(utilities, "writePodspecContents"); - -// await updatePodspecVersion("./Test.podspec", "0.0.2"); -// expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); -// }); -// }); -// describe("updateSourceLocation", () => { -// test("should throw error if there is an error writing file", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// exec.mockReturnValue("commithash"); - -// jest -// .spyOn(utilities, "writePodspecContents") -// .mockImplementationOnce(() => { -// throw new Error("Filesystem Error"); -// }); - -// await expect( -// updateSourceLocation( -// "./Test.podspec", -// "https://github.com/somefork/auto.git" -// ) -// ).rejects.toThrowError( -// "Error updating source location in podspec: ./Test.podspec" -// ); -// }); -// test("should successfully write new source location", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const mock = jest.spyOn(utilities, "writePodspecContents"); - -// exec.mockReturnValue(Promise.resolve("commithash")); - -// await updateSourceLocation( -// "./Test.podspec", -// "https://github.com/somefork/auto.git" -// ); -// expect(mock).lastCalledWith( -// expect.any(String), -// specWithVersion( -// "0.0.1", -// "{ :git => 'https://github.com/somefork/auto.git', :commit => 'commithash' }" -// ) -// ); -// }); -// }); -// describe("validateConfig hook", () => { -// test("should validate options", async () => { -// expect( -// ((await hooks.validateConfig.promise("cocoapods", {})) || []) -// ).toHaveLength(1); -// expect( -// ((await hooks.validateConfig.promise("cocoapods", options)) || []) - -// ).toHaveLength(0); -// expect( -// ((await hooks.validateConfig.promise("cocoapods", multiOptions)) || []) - -// ).toHaveLength(0); -// }); -// }); -// describe("modifyConfig hook", () => { -// test("should set noVersionPrefix to true", async () => { -// const config = {}; -// expect(await hooks.modifyConfig.promise(config as any)).toStrictEqual({ -// noVersionPrefix: true, -// }); -// }); -// }); -// describe("getPreviousVersion hook", () => { -// test("should get previous version from podspec", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// expect(await hooks.getPreviousVersion.promise()).toBe("v0.0.1"); -// }); - -// test("should throw if no version found", async () => { -// mockPodspec(specWithVersion("")); - -// await expect(hooks.getPreviousVersion.promise()).rejects.toThrowError( -// "Version could not be found in podspec: ./Test.podspec" -// ); -// }); - -// test("should get version if multiple podspecs", async () => { -// mockPodspecs([ -// { -// path: "./Test.podspec", -// contents: specWithVersion("0.0.1"), -// }, -// { -// path: "./Test2.podspec", -// contents: specWithVersion("0.0.1"), -// }, -// ]); -// expect(await multiHooks.getPreviousVersion.promise()).toBe("v0.0.1"); -// }); -// }); -// describe("version hook", () => { -// test("should do nothing on dryRun", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const mockLog = jest.spyOn(logger.log, "info"); - -// await hooks.version.promise({ bump: Auto.SEMVER.patch, dryRun: true }); - -// expect(exec).toHaveBeenCalledTimes(0); -// expect(mockLog).toHaveBeenCalledTimes(1); -// }); -// test("should not use logger on quiet dryRun", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const mockLog = jest.spyOn(logger.log, "info"); -// const mockConsole = jest.spyOn(console, "log"); - -// await hooks.version.promise({ -// bump: Auto.SEMVER.patch, -// dryRun: true, -// quiet: true, -// }); - -// expect(exec).toHaveBeenCalledTimes(0); -// expect(mockLog).toHaveBeenCalledTimes(0); -// expect(mockConsole).toHaveBeenCalledTimes(1); -// }); -// test("should version release - patch version", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const mock = jest.spyOn(utilities, "writePodspecContents"); - -// await hooks.version.promise({ bump: Auto.SEMVER.patch }); - -// expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.0.2")); -// }); -// test("should version release - minor version", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const mock = jest.spyOn(utilities, "writePodspecContents"); - -// await hooks.version.promise({ bump: Auto.SEMVER.minor }); - -// expect(mock).lastCalledWith(expect.any(String), specWithVersion("0.1.0")); -// }); -// test("should version release - major version", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const mock = jest.spyOn(utilities, "writePodspecContents"); - -// await hooks.version.promise({ bump: Auto.SEMVER.major }); - -// expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); -// }); -// test("should release version multiple podspecs - patch", async () => { -// mockPodspecs([ -// { -// path: "./Test.podspec", -// contents: specWithVersion("0.0.1"), -// }, -// { -// path: "./Test2.podspec", -// contents: specWithVersion("0.0.1"), -// }, -// ]); -// const mock = jest.spyOn(utilities, "writePodspecContents"); -// await multiHooks.version.promise({ bump: Auto.SEMVER.patch }); -// expect(mock).toHaveBeenCalledTimes(2); -// expect(mock).toHaveBeenNthCalledWith( -// 1, -// "./Test.podspec", -// specWithVersion("0.0.2") -// ); -// expect(mock).toHaveBeenNthCalledWith( -// 2, -// "./Test2.podspec", -// specWithVersion("0.0.2") -// ); -// }); -// test("should release version multiple podspecs - minor", async () => { -// mockPodspecs([ -// { -// path: "./Test.podspec", -// contents: specWithVersion("0.0.1"), -// }, -// { -// path: "./Test2.podspec", -// contents: specWithVersion("0.0.1"), -// }, -// ]); -// const mock = jest.spyOn(utilities, "writePodspecContents"); -// await multiHooks.version.promise({ bump: Auto.SEMVER.minor }); -// expect(mock).toHaveBeenCalledTimes(2); -// expect(mock).toHaveBeenNthCalledWith( -// 1, -// "./Test.podspec", -// specWithVersion("0.1.0") -// ); -// expect(mock).toHaveBeenNthCalledWith( -// 2, -// "./Test2.podspec", -// specWithVersion("0.1.0") -// ); -// }); -// test("should release version multiple podspecs - major", async () => { -// mockPodspecs([ -// { -// path: "./Test.podspec", -// contents: specWithVersion("0.0.1"), -// }, -// { -// path: "./Test2.podspec", -// contents: specWithVersion("0.0.1"), -// }, -// ]); -// const mock = jest.spyOn(utilities, "writePodspecContents"); -// await multiHooks.version.promise({ bump: Auto.SEMVER.major }); -// expect(mock).toHaveBeenCalledTimes(2); -// expect(mock).toHaveBeenNthCalledWith( -// 1, -// "./Test.podspec", -// specWithVersion("1.0.0") -// ); -// expect(mock).toHaveBeenNthCalledWith( -// 2, -// "./Test2.podspec", -// specWithVersion("1.0.0") -// ); -// }); -// test("should throw if there is an error writing new version", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const mock = jest -// .spyOn(utilities, "writePodspecContents") -// .mockImplementation((path, contents) => { -// if (contents.includes("1.0.0")) { -// throw new Error("Filesystem Error"); -// } -// }); - -// await expect( -// hooks.version.promise({ bump: Auto.SEMVER.major }) -// ).rejects.toThrowError( -// "Error updating version in podspec: ./Test.podspec" -// ); - -// expect(mock).lastCalledWith(expect.any(String), specWithVersion("1.0.0")); -// }); -// }); - -// describe("beforeShipit hook", () => { -// test("should call pod lib lint with dryRun flag", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const plugin = new CocoapodsPlugin(options); -// const hook = makeHooks(); - -// plugin.apply({ -// hooks: hook, -// logger: dummyLog(), -// prefixRelease, -// } as Auto.Auto); - -// await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); - -// expect(exec).toBeCalledTimes(1); -// expect(exec).lastCalledWith("pod", ["lib", "lint", "./Test.podspec"]); -// }); -// test("should call pod lib lint for each podspec with dryRun flag", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const plugin = new CocoapodsPlugin(multiOptions); -// const hook = makeHooks(); - -// plugin.apply({ -// hooks: hook, -// logger: dummyLog(), -// prefixRelease, -// } as Auto.Auto); - -// await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); - -// expect(exec).toBeCalledTimes(2); -// expect(exec).toHaveBeenNthCalledWith(1, "pod", [ -// "lib", -// "lint", -// "./Test.podspec", -// ]); -// expect(exec).toHaveBeenNthCalledWith(2, "pod", [ -// "lib", -// "lint", -// "./Test2.podspec", -// ]); -// }); -// test("should call pod lib lint with options with dryRun flag", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const plugin = new CocoapodsPlugin({ -// ...options, -// flags: ["--flag"], -// podCommand: "notpod", -// }); -// const hook = makeHooks(); - -// plugin.apply({ -// hooks: hook, -// logger: dummyLog(), -// prefixRelease, -// } as Auto.Auto); - -// await hook.beforeShipIt.promise({ releaseType: "latest", dryRun: true }); - -// expect(exec).toBeCalledTimes(1); -// expect(exec).lastCalledWith("notpod", [ -// "lib", -// "lint", -// "--flag", -// "./Test.podspec", -// ]); -// }); -// }); - -// describe("canary hook", () => { -// test("should do nothing on dryRun", async () => { -// mockPodspec(specWithVersion("0.0.1")); -// jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); - -// const mockLog = jest.spyOn(logger.log, "info"); - -// await hooks.canary.promise({ -// bump: Auto.SEMVER.patch, -// canaryIdentifier: "canary.1.0", -// dryRun: true, -// }); - -// expect(exec).toHaveBeenCalledTimes(0); -// expect(mockLog).toHaveBeenCalledTimes(1); -// }); -// test("should not use logger on quiet dryRun", async () => { -// mockPodspec(specWithVersion("0.0.1")); -// jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); - -// const mockLog = jest.spyOn(logger.log, "info"); -// const mockConsole = jest.spyOn(console, "log"); - -// await hooks.canary.promise({ -// bump: Auto.SEMVER.patch, -// canaryIdentifier: "canary.1.0", -// dryRun: true, -// quiet: true, -// }); - -// expect(exec).toHaveBeenCalledTimes(0); -// expect(mockLog).toHaveBeenCalledTimes(0); -// expect(mockConsole).toHaveBeenCalledTimes(1); -// }); -// test("should tag with canary version", async () => { -// jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); -// let podSpec = specWithVersion("0.0.1"); -// jest -// .spyOn(utilities, "getPodspecContents") -// .mockImplementation(() => podSpec); -// const mock = jest -// .spyOn(utilities, "writePodspecContents") -// .mockImplementation((path, contents) => { -// podSpec = contents; -// }); - -// const newVersion = await hooks.canary.promise({ -// bump: "minor" as Auto.SEMVER, -// canaryIdentifier: "canary.1.1.1", -// }); - -// expect(newVersion).toBe("0.1.0-canary.1.1.1"); -// expect(exec).toBeCalledTimes(3); -// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); - -// expect(mock).toHaveBeenLastCalledWith( -// expect.any(String), -// specWithVersion( -// "0.1.0-canary.1.1.1", -// "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" -// ) -// ); -// }); -// test("should tag multiple podspeccs with canary version", async () => { -// jest.spyOn(Auto, "getPrNumberFromEnv").mockReturnValue(1); -// const specs = { -// "./Test.podspec": specWithVersion("0.0.1"), -// "./Test2.podspec": specWithVersion("0.0.1"), -// }; -// jest -// .spyOn(utilities, "getPodspecContents") -// .mockImplementation((path) => specs[path]); -// const mock = jest -// .spyOn(utilities, "writePodspecContents") -// .mockImplementation((path, contents) => { -// specs[path] = contents; -// }); - -// const newVersion = await multiHooks.canary.promise({ -// bump: "minor" as Auto.SEMVER, -// canaryIdentifier: "canary.1.1.1", -// }); - -// expect(newVersion).toBe("0.1.0-canary.1.1.1"); -// expect(exec).toBeCalledTimes(6); -// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); -// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test2.podspec"]); -// expect(mock).toBeCalledTimes(4); -// expect(mock).toHaveBeenCalledWith( -// "./Test.podspec", -// specWithVersion( -// "0.1.0-canary.1.1.1", -// "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" -// ) -// ); -// expect(mock).toHaveBeenCalledWith( -// "./Test2.podspec", -// specWithVersion( -// "0.1.0-canary.1.1.1", -// "{ :git => 'https://github.com/intuit-fork/auto.git', :commit => 'undefined' }" -// ) -// ); -// }); -// test("should tag with canary version with no PR number", async () => { -// let podSpec = specWithVersion("0.0.1"); -// jest -// .spyOn(utilities, "getPodspecContents") -// .mockImplementation(() => podSpec); -// const mock = jest -// .spyOn(utilities, "writePodspecContents") -// .mockImplementation((path, contents) => { -// podSpec = contents; -// }); - -// const newVersion = await hooks.canary.promise({ -// bump: "minor" as Auto.SEMVER, -// canaryIdentifier: "canary.1.1.1", -// }); - -// expect(newVersion).toBe("0.1.0-canary.1.1.1"); -// expect(exec).toBeCalledTimes(3); -// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); - -// expect(mock).toHaveBeenLastCalledWith( -// expect.any(String), -// specWithVersion( -// "0.1.0-canary.1.1.1", -// "{ :git => 'https://github.com/intuit/auto.git', :commit => 'undefined' }" -// ) -// ); -// }); -// }); - -// describe("next hook", () => { -// test("should return prerelease versions on dryrun", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const versions = await hooks.next.promise(["0.0.1", "0.0.2"], { -// bump: Auto.SEMVER.minor, -// dryRun: true, -// commits: [], -// fullReleaseNotes: "", -// releaseNotes: "", -// }); -// expect(versions).toStrictEqual(["0.0.1", "0.0.2", "0.1.0-next.0"]); -// }); -// test("should tag with next version", async () => { -// jest.spyOn(Auto, "getCurrentBranch").mockReturnValue("next"); -// const specs = { -// "./Test.podspec": specWithVersion("0.0.1"), -// "./Test2.podspec": specWithVersion("0.0.1"), -// }; -// jest -// .spyOn(utilities, "getPodspecContents") -// .mockImplementation((path) => specs[path]); -// const mock = jest -// .spyOn(utilities, "writePodspecContents") -// .mockImplementation((path, contents) => { -// specs[path] = contents; -// }); - -// const versions = await multiHooks.next.promise([], { -// bump: Auto.SEMVER.major, -// dryRun: false, -// commits: [], -// fullReleaseNotes: "", -// releaseNotes: "", -// }); - -// expect(versions).toContain("1.0.0-next.0"); -// expect(exec).toBeCalledTimes(6); -// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); - -// expect(mock).toBeCalledTimes(2); -// expect(mock).toHaveBeenCalledWith( -// "./Test.podspec", -// specWithVersion("1.0.0-next.0") -// ); -// expect(mock).toHaveBeenCalledWith( -// "./Test2.podspec", -// specWithVersion("1.0.0-next.0") -// ); -// }); -// test("should tag with next version for multiple podspecs", async () => { -// jest.spyOn(Auto, "getCurrentBranch").mockReturnValue("next"); -// let podSpec = specWithVersion("0.0.1"); -// jest -// .spyOn(utilities, "getPodspecContents") -// .mockImplementation(() => podSpec); -// const mock = jest -// .spyOn(utilities, "writePodspecContents") -// .mockImplementation((path, contents) => { -// podSpec = contents; -// }); - -// const versions = await hooks.next.promise([], { -// bump: Auto.SEMVER.major, -// dryRun: false, -// commits: [], -// fullReleaseNotes: "", -// releaseNotes: "", -// }); - -// expect(versions).toContain("1.0.0-next.0"); -// expect(exec).toBeCalledTimes(4); -// expect(exec).toHaveBeenCalledWith("git", ["checkout", "./Test.podspec"]); - -// expect(mock).toHaveBeenLastCalledWith( -// expect.any(String), -// specWithVersion("1.0.0-next.0") -// ); -// }); -// }); - -// describe("publish hook", () => { -// test("should push to trunk if no specsRepo in options", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const plugin = new CocoapodsPlugin(options); -// const hook = makeHooks(); -// plugin.apply({ -// hooks: hook, -// logger: dummyLog(), -// prefixRelease, -// } as Auto.Auto); - -// await hook.publish.promise({ bump: Auto.SEMVER.patch }); - -// expect(exec).toBeCalledTimes(2); -// expect(exec).lastCalledWith("pod", ["trunk", "push", "./Test.podspec"]); -// }); -// test("should push multiple podspecs to trunk if no specsRepo in options", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const plugin = new CocoapodsPlugin(multiOptions); -// const hook = makeHooks(); -// plugin.apply({ -// hooks: hook, -// logger: dummyLog(), -// prefixRelease, -// } as Auto.Auto); - -// await hook.publish.promise({ bump: Auto.SEMVER.patch }); - -// expect(exec).toBeCalledTimes(3); -// expect(exec).toHaveBeenCalledWith("pod", [ -// "trunk", -// "push", -// "./Test.podspec", -// ]); -// expect(exec).toHaveBeenCalledWith("pod", [ -// "trunk", -// "push", -// "./Test2.podspec", -// ]); -// }); - -// test("should push with different pod command if in options", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const plugin = new CocoapodsPlugin({ ...options, podCommand: "notpod" }); -// const hook = makeHooks(); -// plugin.apply({ -// hooks: hook, -// logger: dummyLog(), -// prefixRelease, -// } as Auto.Auto); - -// await hook.publish.promise({ bump: Auto.SEMVER.patch }); - -// expect(exec).toBeCalledTimes(2); -// expect(exec).lastCalledWith("notpod", [ -// "trunk", -// "push", -// "./Test.podspec", -// ]); -// }); - -// test("should push with different pod command with spaces if in options", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const plugin = new CocoapodsPlugin({ -// ...options, -// podCommand: "bundle exec pod", -// }); -// const hook = makeHooks(); -// plugin.apply({ -// hooks: hook, -// logger: dummyLog(), -// prefixRelease, -// } as Auto.Auto); - -// await hook.publish.promise({ bump: Auto.SEMVER.patch }); - -// expect(exec).toBeCalledTimes(2); -// expect(exec).lastCalledWith("bundle", [ -// "exec", -// "pod", -// "trunk", -// "push", -// "./Test.podspec", -// ]); -// }); - -// test("should push to trunk if no specsRepo in options with flags", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const logger = dummyLog(); -// logger.logLevel = "verbose"; - -// const plugin = new CocoapodsPlugin({ -// ...options, -// flags: ["--sources", "someOtherSpecsRepo"], -// }); -// const hook = makeHooks(); -// plugin.apply({ -// hooks: hook, -// logger, -// prefixRelease, -// } as Auto.Auto); - -// await hook.publish.promise({ bump: Auto.SEMVER.patch }); - -// expect(exec).toBeCalledTimes(2); -// expect(exec).lastCalledWith("pod", [ -// "trunk", -// "push", -// "--sources", -// "someOtherSpecsRepo", -// "./Test.podspec", -// "--verbose", -// ]); -// }); - -// test("should push to specs repo if specsRepo in options", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const logger = dummyLog(); -// logger.logLevel = "quiet"; - -// const plugin = new CocoapodsPlugin({ -// ...options, -// specsRepo: "someSpecsRepo", -// }); -// const hook = makeHooks(); -// plugin.apply({ -// hooks: hook, -// logger, -// prefixRelease, -// } as Auto.Auto); - -// await hook.publish.promise({ bump: Auto.SEMVER.patch }); - -// expect(exec).toBeCalledTimes(5); -// expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); -// expect(exec).toHaveBeenNthCalledWith(3, "pod", [ -// "repo", -// "add", -// "autoPublishRepo", -// "someSpecsRepo", -// "--silent", -// ]); -// expect(exec).toHaveBeenNthCalledWith(4, "pod", [ -// "repo", -// "push", -// "autoPublishRepo", -// "./Test.podspec", -// "--silent", -// ]); -// expect(exec).toHaveBeenNthCalledWith(5, "pod", [ -// "repo", -// "remove", -// "autoPublishRepo", -// "--silent", -// ]); -// }); -// test("should push multiple podspecs to specs repo if specsRepo in options", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const logger = dummyLog(); -// logger.logLevel = "quiet"; - -// const plugin = new CocoapodsPlugin({ -// ...multiOptions, -// specsRepo: "someSpecsRepo", -// }); -// const hook = makeHooks(); -// plugin.apply({ -// hooks: hook, -// logger, -// prefixRelease, -// } as Auto.Auto); - -// await hook.publish.promise({ bump: Auto.SEMVER.patch }); - -// expect(exec).toBeCalledTimes(6); -// expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); -// expect(exec).toHaveBeenNthCalledWith(3, "pod", [ -// "repo", -// "add", -// "autoPublishRepo", -// "someSpecsRepo", -// "--silent", -// ]); -// expect(exec).toHaveBeenNthCalledWith(4, "pod", [ -// "repo", -// "push", -// "autoPublishRepo", -// "./Test.podspec", -// "--silent", -// ]); -// expect(exec).toHaveBeenNthCalledWith(5, "pod", [ -// "repo", -// "push", -// "autoPublishRepo", -// "./Test2.podspec", -// "--silent", -// ]); -// expect(exec).toHaveBeenNthCalledWith(6, "pod", [ -// "repo", -// "remove", -// "autoPublishRepo", -// "--silent", -// ]); -// }); - -// test("should push to specs repo if specsRepo in options with flags", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// const logger = dummyLog(); -// logger.logLevel = "veryVerbose"; - -// const plugin = new CocoapodsPlugin({ -// ...options, -// specsRepo: "someSpecsRepo", -// flags: ["--sources", "someOtherSpecsRepo"], -// }); -// const hook = makeHooks(); -// plugin.apply({ -// hooks: hook, -// logger, -// prefixRelease, -// } as Auto.Auto); - -// await hook.publish.promise({ bump: Auto.SEMVER.patch }); - -// expect(exec).toBeCalledTimes(5); -// expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); -// expect(exec).toHaveBeenNthCalledWith(3, "pod", [ -// "repo", -// "add", -// "autoPublishRepo", -// "someSpecsRepo", -// "--verbose", -// ]); -// expect(exec).toHaveBeenNthCalledWith(4, "pod", [ -// "repo", -// "push", -// "--sources", -// "someOtherSpecsRepo", -// "autoPublishRepo", -// "./Test.podspec", -// "--verbose", -// ]); -// expect(exec).toHaveBeenNthCalledWith(5, "pod", [ -// "repo", -// "remove", -// "autoPublishRepo", -// "--verbose", -// ]); -// }); - -// test("should delete autoPublishRepo if it exists and push to specs repo if specsRepo in options", async () => { -// mockPodspec(specWithVersion("0.0.1")); - -// exec = jest.fn().mockImplementation((...args) => { -// if (args[1]?.[1] === "list") { -// return ` -// autoPublishRepo -// - Type: git (master) -// - URL: someSpecsRepo -// - Path: /Users/someUser/.cocoapods/repos/autoPublishRepo - -// master -// - Type: git (master) -// - URL: https://github.com/CocoaPods/Specs.git -// - Path: /Users/someUser/.cocoapods/repos/master - -// trunk -// - Type: CDN -// - URL: https://cdn.cocoapods.org/ -// - Path: /Users/someUser/.cocoapods/repos/trunk -// `; -// } -// }); - -// const plugin = new CocoapodsPlugin({ -// ...options, -// specsRepo: "someSpecsRepo", -// }); -// const hook = makeHooks(); -// plugin.apply({ -// hooks: hook, -// logger: dummyLog(), -// prefixRelease, -// } as Auto.Auto); - -// await hook.publish.promise({ bump: Auto.SEMVER.patch }); - -// expect(exec).toBeCalledTimes(6); -// expect(exec).toHaveBeenNthCalledWith(2, "pod", ["repo", "list"]); -// expect(exec).toHaveBeenNthCalledWith(3, "pod", [ -// "repo", -// "remove", -// "autoPublishRepo", -// ]); -// expect(exec).toHaveBeenNthCalledWith(4, "pod", [ -// "repo", -// "add", -// "autoPublishRepo", -// "someSpecsRepo", -// ]); -// expect(exec).toHaveBeenNthCalledWith(5, "pod", [ -// "repo", -// "push", -// "autoPublishRepo", -// "./Test.podspec", -// ]); -// expect(exec).toHaveBeenNthCalledWith(6, "pod", [ -// "repo", -// "remove", -// "autoPublishRepo", -// ]); -// }); -// }); -// }); diff --git a/plugins/fastlane/package.json b/plugins/fastlane/package.json deleted file mode 100644 index f1b511969..000000000 --- a/plugins/fastlane/package.json +++ /dev/null @@ -1,45 +0,0 @@ -{ - "name": "@auto-it/fastlane", - "version": "10.36.5", - "main": "dist/index.js", - "description": "Use auto to version your pList using fastlane", - "license": "MIT", - "author": { - "name": "Yogesh Khandelwal", - "email": "yogesh_khandelwal@intuit.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" - ], - "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.1.3", - "tslib": "2.1.0" - } -} diff --git a/plugins/fastlane/src/index.ts b/plugins/fastlane/src/index.ts deleted file mode 100644 index 1c0a65212..000000000 --- a/plugins/fastlane/src/index.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { - Auto, - IPlugin, - execPromise, - validatePluginConfiguration, - ILogger, - getPrNumberFromEnv, - DEFAULT_PRERELEASE_BRANCHES, - getCurrentBranch, - determineNextVersion, -} from "@auto-it/core"; - -import { inc, ReleaseType } from "semver"; - -import * as t from "io-ts"; - -import { getpListContents, writepListContents } from "./utilities"; - -const logPrefix = "[Fastlane-Plugin]"; - -/** - * Wrapper to add logPrefix to messages - * - * @param msg - The message to add a prefix to - * @returns Log message with prefix prepended - */ -const logMessage = (msg: string): string => `${logPrefix} ${msg}`; - -const required = t.interface({ - /** Relative path to podspec file */ - pListPath: t.union([t.string, t.array(t.string)]), - /** The command to use for `fastlane` if it needs to be separate like `fastlane action increment_version_numer` */ - fastlaneCommand: t.string, -}); - -const optional = t.partial({ - /** xcode project path */ - xcodeproj: t.string, - -}); - -const pluginOptions = t.intersection([required, optional]); -export type IFastlanePluginOptions = t.TypeOf; - -/** - * Retrieves the version currently in the pList file - * - * @param pListPath - The relative path to the podspec file - */ -export async function getVersion(pListPath: string): Promise { - const pListContents = getpListContents(pListPath); - return await execPromise ("/usr/libexec/PlistBuddy", [ - "-c", - `"Print CFBundleShortVersionString"`, - pListContents, - ]); - - throw new Error(`Version could not be found in podspec: ${pListPath}`); -} - -/** Use auto to version your pList */ -export default class FastlanePlugin implements IPlugin { - /** The name of the plugin */ - name = "fastlane"; - - /** The auto logger */ - logger?: ILogger; - - /** The options of the plugin */ - readonly options: IFastlanePluginOptions; - - /** - * - */ - private get paths() { - if (typeof this.options.pListPath === "string") { - return [this.options.pListPath]; - } - - return this.options.pListPath; - } - - /** Initialize the plugin with it's options */ - constructor(options: IFastlanePluginOptions) { - this.options = options; - } - - /** Tap into auto plugin points. */ - apply(auto: Auto) { - this.logger = auto.logger; - const isQuiet = auto.logger.logLevel === "quiet"; - const isVerbose = - auto.logger.logLevel === "verbose" || - auto.logger.logLevel === "veryVerbose"; - const fastlaneLogLevel = isQuiet ? ["--silent"] : isVerbose ? ["--verbose"] : []; - - auto.hooks.validateConfig.tapPromise(this.name, async (name, options) => { - if (name === this.name || name === `@auto-it/${this.name}`) { - return validatePluginConfiguration(this.name, pluginOptions, options); - } - }); - - auto.hooks.modifyConfig.tap(this.name, (config) => ({ - ...config, - noVersionPrefix: true, - })); - - auto.hooks.getPreviousVersion.tapPromise(this.name, async () => { - return auto.prefixRelease(getVersion(this.paths[0])); - }); - - auto.hooks.version.tapPromise( - this.name, - async ({ bump, dryRun, quiet }) => { - const previousVersion = getVersion(this.paths[0]); - const releaseVersion = inc(previousVersion, bump as ReleaseType); - if (dryRun && releaseVersion) { - if (quiet) { - console.log(releaseVersion); - } else { - auto.logger.log.info(`Would have published: ${releaseVersion}`); - } - - return; - } - - if (!releaseVersion) { - throw new Error( - `Could not increment previous version: ${previousVersion}` - ); - } - - await execPromise("fastlane", [ - "action", - "increment_version_number", - `version_number:${releaseVersion}`, - `xcodeproj:${this.options.xcodeproj}`, - ]) - - await execPromise("git", [ - "commit", - "-am", - `"update version: ${releaseVersion} [skip ci]"`, - "--no-verify", - ]); - - await execPromise("git", [ - "tag", - `${releaseVersion}`, - "-m", - `"Update version to ${releaseVersion}"`, - ]); - } - ); - - auto.hooks.publish.tapPromise(this.name, async () => { - await execPromise("git", [ - "push", - "--follow-tags", - "--set-upstream", - auto.remote, - auto.baseBranch, - ]); - }); - } diff --git a/plugins/fastlane/src/utilities.ts b/plugins/fastlane/src/utilities.ts deleted file mode 100644 index e46baa729..000000000 --- a/plugins/fastlane/src/utilities.ts +++ /dev/null @@ -1,12 +0,0 @@ -import path from "path"; -import fs from "fs"; - -/** - * Retrieves the contents of the podspec file - * - * @param pListPath - The relative path to the podspec file - * @returns A string that is the contents of the file - */ -export function getpListContents(pListPath: string): string { - return fs.readFileSync(path.join(process.cwd(), pListPath)).toString(); -} diff --git a/plugins/fastlane/tsconfig.json b/plugins/fastlane/tsconfig.json deleted file mode 100644 index bfbef6fc7..000000000 --- a/plugins/fastlane/tsconfig.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "extends": "../../tsconfig.json", - "include": ["src/**/*", "../../typings/**/*"], - - "compilerOptions": { - "outDir": "./dist", - "rootDir": "./src", - "composite": true - }, - - "references": [ - { - "path": "../../packages/core" - } - ] -} From 510dcf46811e4fddd3f01d095a45e300b13c6e95 Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Fri, 15 Jul 2022 13:29:33 +0530 Subject: [PATCH 11/12] test --- packages/core/src/semver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/semver.ts b/packages/core/src/semver.ts index fbf7422c7..03dd39fd9 100644 --- a/packages/core/src/semver.ts +++ b/packages/core/src/semver.ts @@ -224,6 +224,7 @@ export function calculateSemVerBump( ? !lastMergedCommitLabels.some((label) => releaseLabels.includes(label)) : lastMergedCommitLabels.some((label) => skipReleaseLabels.includes(label)); console.log("skipRelease Value: ", skipRelease) + if (skipRelease) { return SEMVER.noVersion; } From 43949a19be294f052dc3a63a9d78d2b389487358 Mon Sep 17 00:00:00 2001 From: Yogesh Khandelwal Date: Fri, 15 Jul 2022 14:04:58 +0530 Subject: [PATCH 12/12] fixing build --- packages/core/src/git.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/git.ts b/packages/core/src/git.ts index 2c466cd04..3f58878c0 100644 --- a/packages/core/src/git.ts +++ b/packages/core/src/git.ts @@ -153,7 +153,7 @@ export default class Git { } }, /** wait after abuse */ - onAbuseLimit: (retryAfter: number, opts: ThrottleOpts) => { + onSecondaryRateLimit: (retryAfter: number, opts: ThrottleOpts) => { this.logger.log.error( `Went over abuse rate limit ${opts.method} ${ opts.url