Index by Tag

Filter blog posts by topic

If you work on many projects, you know the pattern: a project gets your full attention for a few weeks, then sits untouched for months until a client comes back with a new request. Meanwhile, its node_modules folder sits on your disk taking up hundreds of megabytes — and so does the one next to it, and the one next to that.

Every few months I'd notice my disk filling up and go hunting through my projects folder, checking sizes and deleting node_modules by hand. It's not hard, it's just tedious. So eventually I wrote a small script to do it for me.

What It Does

nmclean finds every node_modules folder under a directory, shows how much space each one takes, and lets you delete them all at once or decide one by one:

$ nmclean ~/projects
Searching for node_modules in /Users/me/projects ...
  1)    648M  /Users/me/projects/api/node_modules
  2)    580M  /Users/me/projects/web/frontend/node_modules
  3)     92M  /Users/me/projects/old-demo/node_modules

Found 3 folders, 1.3G total.

[a] delete all  [o] one by one  [q] quit: o
(1/3) 648M  /Users/me/projects/api/node_modules  delete? [y/n/q]: n
  Skipped.
(2/3) 580M  /Users/me/projects/web/frontend/node_modules  delete? [y/n/q]: y
  Deleted.
(3/3) 92M  /Users/me/projects/old-demo/node_modules  delete? [y/n/q]: y
  Deleted.

Freed 672M.

Nothing is lost — the next time you open one of those projects, npm install brings everything back.

Why a Bash Script?

  1. No dependencies: It doesn't need Node, npm, or anything else installed. It runs on the bash that ships with macOS and on any Linux machine.
  2. Easy to trust: It's a single file you can read in two minutes. For a tool that deletes folders, I think that matters.
  3. Safe by default: It only ever deletes folders named exactly node_modules, skips nested ones, and asks before deleting anything.

Why I Made It Public

At first this lived in my own scripts folder. Then I started moving some of my work to another machine at home, and as a freelancer I sometimes work on a machine provided by a client. Copying scripts around by hand got old fast, so I pushed it to a public repo. Now installing it anywhere is one line:

curl -fsSL https://raw.githubusercontent.com/OfirYaron/nmclean/main/install.sh | bash

This installs it to ~/.local/bin/nmclean. If you prefer to read the code first (you should!), clone the repo and run ./install.sh instead.

Usage

nmclean              # search the current directory
nmclean ~/projects   # search a specific directory
nmclean -n           # dry run: list sizes, delete nothing
nmclean -y           # delete everything found without asking

If your projects always live in one place, set it as the default in your ~/.zshrc or ~/.bashrc:

export NMCLEAN_ROOT="$HOME/projects"

Pro-Tip

Point it at your projects folder, not your entire home directory. Your home directory also holds node_modules folders that aren't project dependencies — globally installed npm packages (under ~/.nvm, for example) and editor extensions. Start with a dry run (-n) to see what it finds before deleting anything.

What's Next

nmclean is the first in what I plan to be a small collection of utilities I use day to day. If you have an idea for an improvement or a feature you'd like to see, open an issue or a pull request — I'd love for it to grow beyond my own needs.

View nmclean on GitHub →

Home Assistant on a Synology NAS

(in about 777 words)

posted on 11/04/2026 By Ofir Yaron

Your NAS is already running 24/7, has redundant storage, and is significantly more reliable than a Raspberry Pi. There's no reason to run a dedicated device for Home Assistant when a Docker container on your NAS can do the same job — and restart automatically after power cuts, updates, or reboots without any intervention from you.

This guide shows how to deploy it in a way that is correct, minimal, and easy to recover.


Prerequisites

  • Synology NAS with Docker (or Container Manager) installed
  • Portainer running (optional but recommended for visual management)
  • SSH access or File Station access

Step 1: Create the Config Directory

Home Assistant stores all your automations, integrations, and device pairings in its /config directory. This must live on your NAS, outside the container, so it survives container updates and replacements.

Via SSH:

mkdir -p /volume1/docker/homeassistant/config

Or create the same path using Synology File Station.


Step 2: Deploy the Stack

This project uses Docker Compose files committed to Git. The Home Assistant stack lives in this folder.

If you are deploying via Portainer:

  1. Log into Portainer → Stacks → + Add stack
  2. Name it home-assistant
  3. Switch to Upload and upload the docker-compose.yaml from this folder, or paste its contents into the web editor
  4. Click Deploy the stack

If you are deploying directly from the NAS via SSH:

cd /path/to/this/repo/home-assistant
docker compose up -d

The Compose File — What Each Setting Does and Why

services:
  homeassistant:
    image: ghcr.io/home-assistant/home-assistant:stable
    container_name: homeassistant
    hostname: homeassistant
    security_opt:
      - no-new-privileges:true
    healthcheck:
      test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8123/"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 60s
    network_mode: host
    volumes:
      - /volume1/docker/homeassistant/config:/config:rw
    environment:
      TZ: Asia/Jerusalem
    restart: unless-stopped

network_mode: host Home Assistant uses mDNS and Zeroconf to auto-discover devices (smart bulbs, Chromecasts, Apple TV, etc.) on your local network. These protocols do not cross Docker's default bridge network. Host networking is the correct and supported solution, not a workaround.

restart: unless-stopped The container restarts automatically after the NAS reboots or if the process crashes — but it stays stopped if you deliberately stop it. Using always instead would restart the container even after an intentional docker stop, which is the wrong behaviour.

TZ: Asia/Jerusalem Setting the timezone via an environment variable is portable across all NAS platforms. The older pattern of bind-mounting /etc/localtime from the host can break on non-Synology systems or when DST changes are handled differently by the host OS.

security_opt: no-new-privileges:true Prevents any process inside the container from gaining more privileges through setuid/setgid binaries. Correct default for any container that doesn't explicitly need privilege escalation.

No privileged: true The original tutorial enables privileged: true unconditionally. This grants the container full root access to the host kernel — effectively the same as running without any container isolation. Home Assistant does not need this for normal operation. If you have a USB Zigbee or Z-Wave stick, use a devices: entry (commented out in the compose file) to expose only that specific device.


Step 3: Access Home Assistant

Once the container is running and the healthcheck passes:

http://<NAS-IP>:8123

Follow the onboarding flow to create your owner account, set your location, and connect your first devices.


Recovery

Because your config lives at /volume1/docker/homeassistant/config and your compose definition is committed here in Git, recovery after a disk swap or NAS replacement is:

# On the new NAS, after restoring /volume1/docker/homeassistant/config from backup:
git clone <this-repo>
cd Nas-Config/home-assistant
docker compose up -d

That's it. All automations, device pairings, and integrations are in the config directory. Nothing is stored inside the container.


Adding a Zigbee/Z-Wave USB Stick

Uncomment and adjust the devices block in docker-compose.yaml:

    devices:
      - /dev/ttyUSB0:/dev/ttyUSB0

To find the correct device path on your Synology:

ls /dev/tty*

Plug the stick in, run the command again, and the new entry is your device. No privileged: true required.


Using a Reverse Proxy (Caddy, Nginx, etc.)

If you access Home Assistant via a reverse proxy (e.g. Caddy routing homeassistant.yourdomain.com to localhost:8123), Home Assistant will reject the requests by default. There is no environment variable to override this — it must be configured in configuration.yaml.

Add the following to /volume1/docker/homeassistant/config/configuration.yaml:

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 127.0.0.1

use_x_forwarded_for: true Tells Home Assistant to read the real client IP from the X-Forwarded-For header set by the proxy, rather than treating all requests as coming from the proxy itself.

trusted_proxies The list of IPs Home Assistant will accept proxy headers from. Since Caddy runs on the same host with network_mode: host, 127.0.0.1 is sufficient. If your reverse proxy runs in a Docker bridge network instead, also add its subnet (e.g. 172.17.0.0/16).

After saving, restart the container:

docker restart homeassistant

As an Osek Murshe (עוסק מורשה) in Israel, getting paid is a double-edged sword. You see a large sum enter your bank account, but you know deep down that a significant chunk of it doesn't belong to you. It belongs to the Tax Authority (Mas Hachnasa) and the VAT office (Ma'am).

The most common mistake? Treating the Total Invoice as your actual income. By the time the 15th or 23rd of the month rolls around, you've spent the money you were supposed to "put aside."

If you have an iPhone, Mac, or iPad, you can solve this forever with a single tap. Here is how to build your own Israeli Tax Splitter using Apple Shortcuts.

Why Automate Your Taxes?

  1. Mental Clarity: Instantly know your "Real" profit after VAT and Income Tax.
  2. No More Math: Stop dividing by 1.18 on your calculator every time you get a check.
  3. Deadline Awareness: The shortcut tells you exactly how many days you have until the next payment window (23rd for Tax and Ma'am in my case, but you can change to your liking).
  4. One-Tap Access: You can run this from your Home Screen or even by asking Siri.

The Build: Step-by-Step

1. The Math (Back-Calculating the VAT)

Since an Osek Murshe invoice includes 18% VAT, we have to "strip" that VAT first to find your actual income before calculating your 30% income tax.

  • Action: Ask for Input (Set type to Number).
  • Action: Calculate → Provided Input ÷ 1.18.
  • Action: Set Variable → Name it BaseIncome.
  • Action: Calculate → Provided Input − BaseIncome.
  • Action: Set Variable → Name it VATAmount.
  • Action: Calculate → BaseIncome × 0.30.
  • Action: Set Variable → Name it IncomeTax.

2. The Deadline Logic

Israeli tax dates are specific: Income Tax is usually due the 21st of the next month, and Ma'am is due the 23rd of the next Odd month. (In some cases Tax is due every Odd month as well — you may change accordingly.)

For the Income Tax (The 21st):

  1. Action: Adjust Date → Add 1 month to Current Date.
  2. Action: Get Dates from Input → Tap the variable, choose Date Format: Custom, and type MM/21/yyyy.
  3. Action: Get Time Between Dates → Calculate days from Current Date to this new date.
  4. Action: Set Variable → DaysUntilTax.

For the VAT (The Odd-Month 23rd):

  1. Action: Format Date → Use Custom format M (to get the month number).
  2. Action: Calculate → Formatted Date mod 2.
  3. Action: If statement:
    • If calculation is 0 (Even month): Adjust Date (Add 1 month to current date).
    • Otherwise (Odd month): Check if today is after the 23rd; if so, Adjust Date (Add 2 months).
  4. Action: Get Dates from Input → Set format to MM/23/yyyy.
  5. Action: Get Time Between Dates → Find the remaining days and Set Variable to DaysUntilVAT.

3. The Result

Finally, add a Show Result action to display your "Clean" numbers:

💰 Osek Murshe Breakdown
Net Profit: ₪[BaseIncome]

💸 Put Aside Now:
VAT (18%): ₪[VATAmount]
Income Tax (30%): ₪[IncomeTax]

📅 Deadlines:
Income Tax: Due in [DaysUntilTax] days.
VAT (Ma'am): Due in [DaysUntilVAT] days.

The next time a client pays you, don't just smile at the bank notification — run the shortcut, move the "Put Aside" amount to a separate savings sub-account, and sleep better at night knowing you are 100% tax-ready.

Pro-Tip for the Israeli Worker

Add this shortcut as a Widget on your iPhone Home Screen. The next time a client pays you, don't just smile at the bank notification — run the shortcut, move the "Put Aside" amount to a separate savings sub-account, and sleep better at night knowing you are 100% tax-ready.

Grab my shortcut here if you want a head start! But if you have the time, I'd suggest building it from scratch. It's a great way to learn how everything fits together, and it'll make you much more proficient at creating your own tools later on.

Download the Shortcut →

Docker Lifecycle and Building a Dockerfile

(in about 1000 words)

posted on 15/7/2023 By Ofir Yaron

In post, I will explore the lifecycle of Docker and explain how to use it correctly when building a Dockerfile. Docker provides a powerful and efficient way to package, distribute, and run applications in isolated environments called containers. Let's dive in!

Build

The build phase is the first step in the Docker lifecycle. During this phase, a Docker image is created from a Dockerfile. The Dockerfile contains a set of instructions that define the steps to build the image. These instructions can include pulling base images, executing commands, copying files, and configuring the image. Docker reads the Dockerfile and builds a layered image based on the specified instructions. Each instruction in the Dockerfile creates a new layer, allowing for efficient image sharing and faster builds when changes are made.

Image

After a successful build, an image is created. An image serves as a static, read-only template that contains all the necessary instructions for creating a container. It includes the application or service code, the runtime environment, required libraries, and dependencies needed to run the application. Images are portable and can be shared across different environments and systems, ensuring consistent execution.

Container

A container is an instance of an image that runs as a separate process. Containers provide isolation and encapsulation, allowing applications to run consistently across different environments. They can be started, stopped, paused, and deleted. When you start a container from an image, Docker creates a new container instance based on the specified image, runs it as a process, and provides an isolated runtime environment. Containers have their file systems, network interfaces, and process trees, making them lightweight and independent units of deployment.

Run

To start a container from an image, you can use the docker run command. This command creates a new container instance based on the specified image and runs it as a process. It also provides options to configure the container's runtime behavior, such as network settings, environment variables, port mappings, and resource constraints. Running a container allows you to interact with the application or service it contains.

Lifecycle States

Containers can exist in different states throughout their lifecycle. Let's explore these states:

  • Running: A container is in the running state when its main process or application is actively executing.
  • Paused: Containers can be paused, temporarily suspending their processes without stopping them. Pausing a container allows you to save resources and quickly resume its execution.
  • Stopped: When a container is stopped, it is no longer running, and its processes are halted. Stopped containers can be restarted or removed.
  • Exited: When a container's main process completes or encounters an error and terminates, it enters the exited state. Exited containers can be restarted or removed.

Managing Containers and Images

Docker provides a set of commands to manage containers and images:

  • Manage Containers: You can use commands like docker start, docker stop, docker pause, docker unpause, docker restart, and docker rm to manage containers. These commands allow you to start, stop, pause, resume, restart, and remove containers as needed.

  • Manage Images: Docker images can be managed using commands such as docker build, docker pull, docker push, docker tag, and docker rmi. These commands enable you to build new images, pull existing images from a registry, push images to a registry, tag images with versions or labels, and remove images when they are no longer needed.


Dockerfile Instructions

When writing a Dockerfile, you have access to various instructions that help define the image's build process and runtime behavior. Let's take a look at some commonly used instructions and their purposes:

  • FROM: Specifies the base image to build upon. It defines the starting point for your image and can be any existing Docker image available from a registry.
  • ARG: Defines build-time variables that can be passed to the builder with the --build-arg flag during the docker build command. ARG values are accessible during the build process but not saved in the final image.
  • ENV: Sets environment variables within the image. ENV instructions can define variables that will be available during the container's runtime. They are also useful for configuring the runtime behavior of the image.
  • RUN: Executes commands during the build process. You can use RUN instructions to install packages, run scripts, compile code, or perform any necessary build-time actions. The commands specified in RUN instructions are executed in the build environment.
  • COPY and ADD: These instructions allow you to copy files or directories from the host machine into the image. COPY is used to copy local files, while ADD has additional functionality like unpacking archives and fetching remote URLs.
  • WORKDIR: Sets the working directory for subsequent instructions in the Dockerfile. It affects commands like RUN, CMD, and ENTRYPOINT, allowing you to define a specific directory within the image where these commands will be executed.
  • EXPOSE: Informs Docker that the container listens on specific network ports at runtime. It documents the ports on which the container is expected to listen for incoming connections.
  • CMD: Specifies the default command to run when the container starts. CMD provides the command and its arguments, which can be overridden during container execution. There can be only one CMD instruction in a Dockerfile.
  • ENTRYPOINT: Defines the primary command that should be executed when the container starts. It is similar to CMD but allows you to specify a command that cannot be overridden by providing arguments during container execution. ENTRYPOINT can be used in combination with CMD to provide default arguments.

Throughout the lifecycle, you can make changes to the Dockerfile, rebuild the image, and recreate or update containers based on the updated image. This flexibility allows for easy iteration and maintenance of your application or service.

Remember, containers are designed to be disposable, as such it's important understand the lifecycle and use Dockerfile installation routine correctly so the important data is not part of the image creating and is able to be persisted per container.

Supercharge Your Workflow with Aliases

(in about 500 words)

posted on 20/5/2023 By Ofir Yaron

Boost Your Productivity with Aliases

As a developer or DevOps engineer, you're likely familiar with those long and repetitive commands you use frequently in your terminal. They can be time-consuming and prone to typos, which hampers your productivity. But fear not! There's a solution: aliases.

In this article, we'll explore how aliases can speed up your workflow, making your life easier and more efficient.

Why Aliases? Aliases allow you to create custom shortcuts for commonly used commands, making them shorter and easier to remember. With aliases, you can eliminate the need to type lengthy commands repeatedly, saving you time and reducing the chances of errors. They offer flexibility and convenience, enabling you to streamline your day-to-day tasks and improve your productivity.



Creating an Alias:

To create an alias, you'll need to edit your shell configuration file, typically ~/.bash_profile, ~/.bashrc or ~/.zshrc. This file contains settings and configurations for your terminal session. Let's say you have a long command you frequently use:

longcommand --option1 value1 --option2 value2 argument

You can create an alias for it using the following steps:

  • Open your terminal application.
  • Locate your shell configuration file, typically ~/.bash_profile, ~/.bashrc or ~/.zshrc, and open it with a text editor.
  • Add the following line to define your alias:
alias shortcommand='longcommand --option1 value1 --option2 value2 argument'

(Replace shortcommand with the desired name for your alias.)

  • Save the configuration file and close the text editor.
  • Reload the configuration file in the current terminal session:
source ~/.bash_profile

Using Parameters in Aliases:

What if you want your alias to accept dynamic parameters? Fortunately, aliases can be defined as functions, allowing you to pass arguments. Let's consider the previous example with parameters:

  • Open your terminal application.
  • Locate your shell configuration file and open it with a text editor.
  • Add the following lines to define your alias as a function:
mycommand() {
    longcommand --option1 "$1" --option2 "$2"
}

In this example, mycommand is the alias name, defined as a function that accepts two parameters ("$1" and "$2"").

  • Adjust the command and parameters according to your requirements.
  • Save the configuration file and close the text editor.
  • Reload the configuration file in the current terminal session ans before:
source ~/.bash_profile

Now, you can use the alias with parameters just like any other command:

mycommand value1 value2

Summary:

By leveraging aliases and command shortcuts, you can significantly enhance your workstation and optimize your day-to-day tasks. They provide a simple, yet powerful way to reduce typing, eliminate errors, and save time. Whether you're a developer or a DevOps engineer, incorporating aliases into your workflow will boost your productivity and improve your overall development experience.

So, go ahead and start creating aliases for your frequently used commands. Tailor them to your specific needs and watch your efficiency soar.

Professional Tip, you can also host and share them using a public repository on GitHub, that way you can download and install them on any machine you work on.

Automate EFS Creation with Bash Scripting

(in about 335 words)

posted on 16/4/2023 By Ofir Yaron

Amazon Elastic File System (EFS) is a scalable, highly available, and secure file system designed to provide shared access to data across multiple instances in the cloud. In this tutorial, I'll show you how to create a new EFS drive and all its required assets using a bash script so that it can be connected to a predefined EC2 machine.

Prerequisites:

  • AWS CLI installed and configured with proper credentials
  • EC2 machine and VPC in place

Step 1: Set Variables

First, we'll set the variables we need to run the script. Replace REGION, EC2_NAME, and EFS_NAME with your desired values.

REGION=eu-central-1
EC2_NAME=MY_MACHINE
EFS_NAME=my_shared_efs_drive

Step 2: Get EC2 Instance ID

Using the AWS CLI, we'll get the EC2 instance ID by EC2 instance name:

EC2ID=$(aws ec2 describe-instances --filters "Name=tag:Name,Values=$EC2_NAME" \
--query 'Reservations[*].Instances[*].{ID:InstanceId}' \
--output text \
--region $REGION)
echo EC2 instance ID: $EC2ID

Step 3: Get VPC and Subnet IDs

Next, we'll get the VPC and subnet IDs for the EC2 instance:

VPCID=$(aws ec2 describe-instances --instance-ids $EC2ID \
--query 'Reservations[*].Instances[*].{VPC:VpcId}' \
--output text \
--region $REGION)
echo VPC ID: $VPCID

SUBNETID=$(aws ec2 describe-instances --instance-ids $EC2ID \
--query 'Reservations[*].Instances[*].{Subnet:SubnetId}' \
--output text \
--region $REGION)
echo Subnet ID: $SUBNETID

Step 4: Create Security Groups

We'll create two security groups - one for the EC2 instance and one for the mount target - and allow ingress traffic for each:

EC2_SGID=$(aws ec2 create-security-group \
--region $REGION \
--group-name efs-ec2-sg \
--description "Amazon EFS SG for EC2 instance" \
--vpc-id $VPCID \
--query 'GroupId' \
--output text)
echo EC2 SG ID: $EC2_SGID

MT_SGID=$(aws ec2 create-security-group \
--region $REGION \
--group-name efs-mt-sg \
--description "Amazon EFS SG for mount target" \
--vpc-id $VPCID \
--query 'GroupId' \
--output text)
echo Mount Target SG ID: $MT_SGID

aws ec2 authorize-security-group-ingress \
--group-id $EC2_SGID \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0 \
--region $REGION

aws ec2 authorize-security-group-ingress \
--group-id $MT_SGID \
--protocol tcp \
--port 2049 \
--source-group $EC2_SGID \
--region $REGION

Step 5: Create EFS Drive

Now we can create the EFS drive using the following command:

FSID=$(aws efs create-file-system \
--encrypted \
--creation-token $EFS_NAME \
--tags Key=Name,Value=$EFS_NAME \
--region

Mac Development Tools

(in about 628 words)

posted on 4/4/2023 By Ofir Yaron

A friend of mine recently bought a new MacBook Pro and wants to dive into the world of development. He asked me to recommend some development essentials, so in the hope of helping others in the same position, let's get started.

First of all, congratulations on joining the elite club of Mac users! You now have a fancy new machine, but it's time to put it to good use beyond binge-watching Netflix. Why not try your hand at development? Before you begin, let's discuss some essential tools that will help you become the coolest kid on the block (or at least the most productive one).

As a developer, having the right tools at your disposal can significantly improve your productivity and workflow. From code editors and version control systems to command-line utilities and productivity apps, there are many tools that can help you work faster and more efficiently.

In this post, I'll cover some of the essential developer tools for Mac users, including both well-known and lesser-known tools that can make your life as a developer easier. Whether you're a beginner or an experienced developer, this roundup is sure to include something useful for you.

Code Editors:

One of the most important tools for any developer is a code editor. Mac users have several great options to choose from, including:

  • Visual Studio Code: a free and open-source code editor that supports a wide range of languages and extensions, with features like built-in Git integration and debugging tools.
  • Sublime Text: a lightweight and fast code editor with a powerful set of features, including multi-line editing, project management, and support for plugins.
  • Atom: a customizable and hackable code editor that is also free and open-source, with features like package management, Git integration, and syntax highlighting.

Version Control Systems:

Version control systems are essential for managing changes to your code and collaborating with others. Some popular options for Mac users include:

  • Git: a widely used version control system that allows you to track changes to your code, collaborate with others, and manage your projects.
  • Subversion (SVN): another version control system that is particularly popular in corporate environments, with features like centralized repositories and user authentication.

Package Managers:

Package managers make it easy to install and manage software packages and libraries. Some popular options for Mac users include:

  • Homebrew: a package manager that allows you to install and manage a wide range of software packages, libraries, and utilities.
  • MacPorts: a similar package manager that is focused on providing Unix and Linux utilities for Mac users.

Collaboration Tools:

Collaboration tools are essential for working with others on software development projects. Some popular options for Mac users include:

  • Slack: a popular team communication platform that allows you to chat, share files, and collaborate with others in real-time.
  • Trello: a visual project management tool that allows you to organize your tasks and collaborate with others on your projects.

Productivity Tools:

Finally, productivity tools can help you manage your time and stay focused on your work. Some popular options for Mac users include:

  • Alfred: a productivity app that allows you to launch apps, search your files, and perform other tasks using keyboard shortcuts.
  • RescueTime: a time-tracking app that helps you monitor your productivity and identify areas where you can improve your workflow.



In conclusion, these are just a few of the essential developer tools for Mac users that can help you work more efficiently and effectively. Whether you're a beginner or an experienced developer, using the right tools can make a big difference in your productivity and workflow. So take some time to explore these tools and find the ones that work best for you!

Useful Bash Commands

(in about 852 words)

posted on 2/4/2023 By Ofir Yaron

By mastering commonly used Bash commands and exploring more advanced ones, you can become a more efficient and effective user of the command line. By automating repetitive tasks with scripts, you can reduce the risk of mistakes and free up your time to focus on more productive work. In this post, we'll cover a range of useful Bash commands and show you how to use them to streamline your workflow and automate tasks.

Basic Commands

  • ls: Lists the contents of the current directory.
  • cd: Changes the current working directory.
  • pwd: Prints the current working directory.
  • mkdir: Creates a new directory.
  • rmdir: Deletes an empty directory.
  • touch: Creates a new empty file or updates the modification time of an existing file.

File Manipulation Commands

  • cp: Copies a file from one location to another.
  • mv: Moves or renames a file or directory.
  • rm: Deletes a file or directory.
  • cat: Displays the contents of a file.
  • grep: Searches for a specific pattern in a file.
  • head: Displays the first few lines of a file.
  • tail: Displays the last few lines of a file.
  • diff: Compares two files and displays the differences.

System Information Commands

  • top: Displays real-time information about the processes running on the system.
  • ps: Shows information about the processes currently running on the system.
  • df: Displays information about disk usage on the system.
  • free: Shows information about the system's memory usage.
  • uname: Prints information about the system, such as the kernel version and operating system name.

Network Commands

  • ping: Tests connectivity to a specific host or IP address.
  • nslookup: Looks up the IP address of a specific host.
  • wget: Downloads files from the internet.
  • curl: Sends HTTP requests and displays the response.
  • These are just a few examples of commonly used Bash commands.

Advanced Commands

  • find: Searches for files and directories that match a specific set of criteria. For example, to find all files with a .txt extension in the current directory and its subdirectories, you can use:
find . -name "**.txt"
  • grep: Searches for a specific pattern in a file. To search for the string "hello" in all .txt files in the current directory, you can use:
grep "hello" **.txt
  • sed: Stream editor for filtering and transforming text. To replace all occurrences of the string "foo" with "bar" in a file, you can use:
sed 's/foo/bar/g' myfile.txt
  • awk: Powerful text-processing tool for manipulating structured data. To print the second column of a CSV file, you can use:
awk -F ',' '{print $2}' myfile.csv

Pipes

Pipes (|) are used to redirect the output of one command as input to another command. This allows you to chain together commands to perform more complex operations. Here are a few examples:

To find all files with a .txt extension and count the number of lines in each file, you can use:

find . -name "*.txt" | xargs wc -l

The find command searches for all .txt files, and the output is piped to xargs, which passes each file as an argument to the wc -l command to count the number of lines. To search for a specific string in all .txt files in the current directory and display the matching lines, you can use:

grep "search string" *.txt | awk '{print "File: " $2 " - " $0}'

The grep command searches for the specified string in all .txt files, and the output is piped to awk, which formats the output to display the matching lines with the corresponding file name. Pipes are a powerful tool for combining and manipulating data in the command line, and mastering them can greatly improve your productivity as a Bash user.

Additional Useful Commands

Get Local IP

curl ifconfig.me

Get IP from terminal:

wget -qO- https://ipecho.net/plain ; echo

Get response from EKS pod by it’s IP:

wget -qO- http://10.1.1.249:8080/ ; echo

Handy git pull:

find . -type d -depth 1 -exec git --git-dir={}/.git --work-tree=$PWD/{} pull \;

This bash command uses the find command to search for directories (-type d) in the current directory (.), at a depth of 1 (-depth 1). For each directory found, it runs the git pull command in that directory (-exec git --git-dir={}/.git --work-tree=$PWD/{} pull ;).

Here's a breakdown of what each part of the command does:

  • find .: Search for files/directories in the current directory (".")
  • type d: Only look for directories
  • depth 1: Only look at directories that are one level deep (i.e., not recursive)
  • exec: Run the following command for each directory found
  • git --git-dir={}/.git --work-tree=$PWD/{} pull: This runs the git pull command in each directory found by find. The {} is a placeholder for the directory name found by find, so for each directory found, the command substitutes {} with the directory name. The --git-dir option specifies the path to the .git directory for the repository, and the --work-tree option specifies the working tree (i.e., the top-level directory of the repository). ;: The semicolon indicates the end of the command passed to -exec.

In summary, this command searches for directories in the current directory that contain a Git repository, and runs git pull in each repository to fetch and merge any changes from the remote repository.

Terraform Modules

(in about 364 words)

posted on 21/2/2023 By Ofir Yaron

Terraform Modules

A Terraform module is a reusable piece of Terraform code that encapsulates a set of related resources and their configuration. Modules can be used to create, modify, and delete resources as a single unit, making it easy to manage complex infrastructure components. Modules can also be shared across different projects, making it easy to reuse code and standardize resource configurations across different environments.

A module consists of a directory containing one or more Terraform configuration files. The directory can also contain other files, such as variables, outputs, and scripts. A module can be published to a public or private registry or stored locally.

Here is an example of how to create a Terraform module that creates an Amazon Elastic Container Service (ECS) cluster in an AWS infrastructure as code project:

provider "aws" {
  region = var.region
}

resource "aws_ecs_cluster" "cluster" {
  name = var.name
}

variable "region" {
  type    = string
  default = "us-west-2"
}

variable "name" {
  type    = string
  default = "my-cluster"
}

In the example above, we have created a module that creates an ECS cluster using the AWS provider. We have defined two variables (region and name) that can be customized when the module is used. The module consists of one resource (aws_ecs_cluster) that creates an ECS cluster with the specified name.

Using Terraform Modules

Using a Terraform module in an AWS infrastructure as code project involves the following steps:

Declare the module: In your main Terraform configuration file, you need to declare the module and provide its source location. You can declare the module using the module block and specify the source location using the source attribute.

provider "aws" {
  region = "us-west-2"
}

module "ecs-cluster" {
  source = "./module/ec2-cluster"
  name   = "my-cluster"
}



Customize the variables

You can customize the variables defined in the module by specifying their values in the module block.

provider "aws" {
  region = "us-west-2"
}

module "ecs-cluster" {
  source = "./module/ec2-cluster"
  name   = "my-cluster"
  region = "us-east-1"
}



Use the module resources

Once the module has been declared and customized, you can use its resources in your main configuration file by referencing them using their output values.

provider "aws" {
  region = "us-west-2"
}

module "ecs-cluster" {
  source = "./module/ec2-cluster"
  name   = "my-cluster"
  region = "us-east-
}

Tampermonkey - common function

(in about 272 words)

posted on 1/9/2022 By Ofir Yaron

Tampermonkey - Automate your boring repetitive online actions

Tampermonkey is a very popular userscript manager, it makes it very easy to create scripted automations, manage your userscripts and it's available for Chrome, Microsoft Edge, Safari, Opera Next, and Firefox.

personally I use it mostly to automate my repetitive online actions, but I did use it as a developer to script a webpage funnel and have it run with the click of a button instead of do going through the website funnel manually every time just to check a mini feature at the end of the funnel.

To install [Tampermonkey](https://www.tampermonkey.net/)

since Tampermonkey script use Vanila javascript, over time I've found my self recreating these basic utility function that can come handy:

const mouseEvent = new Event('dblclick', {
  'view': window,
  'bubbles': true,
  'cancelable': true
});

Changing an input value that also handles React states:

  function dispatchValueChange(input, newValue){
    let lastValue = input.value;
    input.value = newValue;
    let event = new Event('input', { bubbles: true });
    event.simulated = true;
    let tracker = input._valueTracker;
    if (tracker) {
        tracker.setValue(lastValue);
    }
    input.dispatchEvent(event);
  }
  function dispatchValueChangeById(elementId, newValue){
    const element = document.getElementById(elementId);
    dispatchValueChange(element, newValue);
  }

An additional method (based on **dispatchValueChange**) to change the value by a css selector ```javascript function dispatchValueChangeBySelector(elementSelector, newValue){ const elements = document.querySelectorAll(elementSelector); if (elements.length) { dispatchValueChange(elements[0], newValue); } } ```

Wait functions to have a proper wait when changing rendered page:

  function waitForElementById(elementId) {
    return new Promise((resolve) => {
      const checkExist = setInterval(function () {
        if (document.getElementById(elementId)) {
            clearInterval(checkExist);
            resolve();
        }
      }, 300);
    });
  }
  function waitForElementBySelector(elementSelector) {
    return new Promise((resolve) => {
      const checkExist = setInterval(function () {
        if (document.querySelectorAll(elementSelector).length) {
          clearInterval(checkExist);
          resolve();
        }
    }, 300);
    });
  }

Click an element by a css selector ```javascript function clickBySelector(elementSelector) { const elements = document.querySelectorAll(elementSelector); if (elements.length) { elements[0].click(); } } ```

Markdown Blog - Roadmap

(in about 327 words)

posted on 31/8/2022 By Ofir Yaron

Blog Component

The component content will be based on Markdown files to make it natural for a developer to maintain. the markdown files will be loaded and styled, added with a Copy-to-Clipboard button and a better code area styling necessary for a dev blog.

Roadmap:

  • Create a new Page and instrument it into the website menu, include basic styling.
  • create Article component, load the markdown and apply basic styling
  • Add Code Component to render instead of the code markdown block.
  • Add Copy-Paste Icon and apply functionality.
  • Test the look&feel of a 'Dark' Mode

Stories:

Blog Page Skeleton

  1. Add the menu item "Blog"
  {
    label: 'Blog',
    path: '/blog',
  }
  1. Add the page component
  2. Add basic styling

Create Article component

  1. Add Article component to show a single post

Loading Markdown

  1. Load the markdown using markdown-to-jsx Library

  2. Extract hidden fields from the Markdown


Markdown do not have a well defined standard for non visible metadata, one option is to hide them as follows: ``` [_metadata_:]:- """ ... ```
then by using this regex we are able to extract them out: ``` const metadataRegex = /_metadata_:(?(\w)*)(]:-\s")(?[\w\s\d/'+-]*)(")/g; ```
Afterwards we can use the function to extend the markdown and return a more elaborated object holding the markdown, word count and additional metadata fields: ```javascript const collectMetatada = (markdown) => { const count = markdown.split(/\s+/) .map((s) => s.replace(/\W/g, '')) .filter((s) => s.length).length; const metadata = [...markdown.matchAll(metadataRegex)] .reduce((acc, match) => ({ ...acc, [match[1]]: match[4] }), {}); return { ...metadata, wordcount: count, markdown }; }; ```
  1. Place inside Article Component
  2. Style the Article frame

Useful links: