Problem
While working on a project, I came across an interesting issue. Multiple TypeScript and bash scripts needed to read from or write to a shared file. Thus, the scripts needed the file path (directly or indirectly). The question is: if the file location changes, how should I structure the code such that only one file needs to be updated (and thus reduce the risk of missing updates in various scripts)?
Attempt 1: Share a utils.ts file for Typescript files
The Typescript scripts could import the file path in a utils.ts
and use a getSharedFilePath
function e.g.
import { getSharedFilePath } from "utils";
const SHARED_PATH = getSharedFilePath();
BUT! What about the bash scripts? They still hardcoded the path e.g.
SHARED_PATH="path/to/shared/file.json";
Pros:
- ✅ DRY for Typescript files (but not for bash scripts)
Cons:
- ❌ Violates DRY principle. Changing the file location means updating the
utils.ts
and each bash script. - ⚠️ High risk of inconsistency or human error.
Attempt 2: Pass Full Path as Env Var
Instead of setting the path in each bash script, set it as an env var like SHARED_PATH
.
jobs:
deploy:
env:
SHARED_PATH: path/to/shared/file.json
steps:
- run: npx hardhat run scripts/deploy.ts
Then in your TypeScript:
const { SHARED_PATH } = process.env;
And in your bash scripts, just call it e.g.:
set -u # throw error if script references an undefined variable e.g. SHARED_PATH
echo $SHARED_PATH
Pros:
- ✅ DRY: Scripts are isolated from changes to the file path value i.e. the file structure and file name can change without requiring script updates.
- ✅ Keeps configurable data at high levels: env vars
- ✅ Consistent, uniform mechanism for both TypeScript and bash scripts to access the file path
Cons:
Here’s your list with fitting emojis prepended:
- ⚠️ You lose
path.join
safety in TypeScript. That said, one could argue that environment variables should know the correct delimiter since they ought to be tailored to the environment. -
♻️ The env var either:
- 🔁 Must be duplicated in every job that needs it, or
- 🧪 Set at the workflow level, potentially polluting jobs that don’t use it, or
- 🧩 Require a composite action to centralize, which adds complexity. At this point, would it be over-engineered?
🔧 Dependency on env var and need for error handling to check that it's set correctly
Another option: Pass in the path as a parameter
While I didn't implement this, I could also have used hardhat tasks instead of scripts.
Pros:
- ✅ DRY: within scripts (but not the interface calling the script)
- ✅ Scripts are isolated from changes to the file path value
Cons:
- 🚫 Opposes the principle of minimising function arguments
- ⚙️ Hardhat tasks require more setup than hardhat scripts
- 🔁 The parameters must be passed in every time the hardhat task or bash script is called
Conclusion
Using a shared Typescript utils file is insufficient. It only solves the problem for a subset of your codebase (TypeScript) and introduces inconsistency. Passing in the path as a parameter keeps code within scripts DRY, but requires adding an argument to the interface of every script that needs the path.
The solution I chose was to centralise the file path as a single env var. This provides the cleanest interface for all scripts — especially when dealing with both TypeScript and bash. It decouples the file path from script internals, which is valuable when the directory structure or file name may change.
Have a different strategy to manage shared variables across different languages? Share your knowledge or suggest improvements in the comments.
Top comments (0)