diff --git a/.changeset/eight-beans-visit.md b/.changeset/eight-beans-visit.md new file mode 100644 index 00000000000..5a6af9c839b --- /dev/null +++ b/.changeset/eight-beans-visit.md @@ -0,0 +1,5 @@ +--- +"@smithy/shared-ini-file-loader": patch +--- + +read config files from paths relative to homedir diff --git a/packages/shared-ini-file-loader/src/loadSharedConfigFiles.spec.ts b/packages/shared-ini-file-loader/src/loadSharedConfigFiles.spec.ts index b966188d055..1ff7e332dc8 100644 --- a/packages/shared-ini-file-loader/src/loadSharedConfigFiles.spec.ts +++ b/packages/shared-ini-file-loader/src/loadSharedConfigFiles.spec.ts @@ -1,6 +1,7 @@ import { getConfigData } from "./getConfigData"; import { getConfigFilepath } from "./getConfigFilepath"; import { getCredentialsFilepath } from "./getCredentialsFilepath"; +import { getHomeDir } from "./getHomeDir"; import { loadSharedConfigFiles } from "./loadSharedConfigFiles"; import { parseIni } from "./parseIni"; import { slurpFile } from "./slurpFile"; @@ -10,6 +11,7 @@ jest.mock("./getConfigFilepath"); jest.mock("./getCredentialsFilepath"); jest.mock("./parseIni"); jest.mock("./slurpFile"); +jest.mock("./getHomeDir"); describe("loadSharedConfigFiles", () => { const mockConfigFilepath = "/mock/file/path/config"; @@ -18,6 +20,7 @@ describe("loadSharedConfigFiles", () => { configFile: mockConfigFilepath, credentialsFile: mockCredsFilepath, }; + const mockHomeDir = "/users/alias"; beforeEach(() => { (getConfigFilepath as jest.Mock).mockReturnValue(mockConfigFilepath); @@ -25,6 +28,7 @@ describe("loadSharedConfigFiles", () => { (parseIni as jest.Mock).mockImplementation((args) => args); (getConfigData as jest.Mock).mockImplementation((args) => args); (slurpFile as jest.Mock).mockImplementation((path) => Promise.resolve(path)); + (getHomeDir as jest.Mock).mockReturnValue(mockHomeDir); }); afterEach(() => { @@ -49,6 +53,20 @@ describe("loadSharedConfigFiles", () => { expect(getCredentialsFilepath).not.toHaveBeenCalled(); }); + it("expands homedir in configFile and credentialsFile from init if defined", async () => { + const sharedConfigFiles = await loadSharedConfigFiles({ + filepath: "~/path/credentials", + configFilepath: "~/path/config", + }); + expect(sharedConfigFiles).toStrictEqual({ + configFile: "/users/alias/path/config", + credentialsFile: "/users/alias/path/credentials", + }); + expect(getHomeDir).toHaveBeenCalled(); + expect(getConfigFilepath).not.toHaveBeenCalled(); + expect(getCredentialsFilepath).not.toHaveBeenCalled(); + }); + describe("swallows error and returns empty configuration", () => { it("when readFile throws error", async () => { (slurpFile as jest.Mock).mockRejectedValue("error"); diff --git a/packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts b/packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts index 9bf6336e877..21255090def 100644 --- a/packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts +++ b/packages/shared-ini-file-loader/src/loadSharedConfigFiles.ts @@ -1,8 +1,10 @@ import { Logger, SharedConfigFiles } from "@smithy/types"; +import { join } from "path"; import { getConfigData } from "./getConfigData"; import { getConfigFilepath } from "./getConfigFilepath"; import { getCredentialsFilepath } from "./getCredentialsFilepath"; +import { getHomeDir } from "./getHomeDir"; import { parseIni } from "./parseIni"; import { slurpFile } from "./slurpFile"; @@ -39,15 +41,27 @@ export const CONFIG_PREFIX_SEPARATOR = "."; export const loadSharedConfigFiles = async (init: SharedConfigInit = {}): Promise => { const { filepath = getCredentialsFilepath(), configFilepath = getConfigFilepath() } = init; + const homeDir = getHomeDir(); + const relativeHomeDirPrefix = "~/"; + + let resolvedFilepath = filepath; + if (filepath.startsWith(relativeHomeDirPrefix)) { + resolvedFilepath = join(homeDir, filepath.slice(2)); + } + + let resolvedConfigFilepath = configFilepath; + if (configFilepath.startsWith(relativeHomeDirPrefix)) { + resolvedConfigFilepath = join(homeDir, configFilepath.slice(2)); + } const parsedFiles = await Promise.all([ - slurpFile(configFilepath, { + slurpFile(resolvedConfigFilepath, { ignoreCache: init.ignoreCache, }) .then(parseIni) .then(getConfigData) .catch(swallowError), - slurpFile(filepath, { + slurpFile(resolvedFilepath, { ignoreCache: init.ignoreCache, }) .then(parseIni)