Skip to content

Instantly share code, notes, and snippets.

@Tobiaqs
Last active March 25, 2024 10:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Tobiaqs/5f378ba12bbd946045c18c671ba95dbb to your computer and use it in GitHub Desktop.
Save Tobiaqs/5f378ba12bbd946045c18c671ba95dbb to your computer and use it in GitHub Desktop.
Version pinning for docker-compose
#!/usr/bin/env python3
import json
import os
import subprocess
import sys
import yaml
if len(sys.argv) <= 1:
print(
"""
Run this script in your docker project folder (where
docker-compose.yml resides)
Possible flags:
Update hashes based on local Docker inventory. docker-compose.yml
and related Dockerfiles will be written to.
-u, --update
Strip (get rid of any hashes in docker-compose.yml and Dockerfiles)
-s, --strip
Update local Docker image inventory (similar to pull, but also pulls
images referenced in context Dockerfiles)
-p, --pull
To update all image hashes, run dhashes -p && dhashes -u
"""
)
exit()
pull = False
strip = False
if len(sys.argv) > 1 and (sys.argv[1] == "--pull" or sys.argv[1] == "-p"):
pull = True
if len(sys.argv) > 1 and (sys.argv[1] == "--strip" or sys.argv[1] == "-s"):
strip = True
def get_new_image(image):
image = image.split("@")[0]
if strip or pull:
return image
else:
inspection = json.loads(
subprocess.run(
["docker", "inspect", image],
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
).stdout
)
if not inspection:
raise RuntimeError("Run pull first, not all images are present locally")
return image + "@" + inspection[0]["RepoDigests"][0].split("@")[1]
encountered_stripped_images = []
for dc_file in filter(lambda p: p.startswith("docker-compose"), os.listdir()):
with open(dc_file, "r") as f:
yaml_str = f.read()
doc = yaml.safe_load(yaml_str)
yaml_replacements = []
for obj in doc["services"].values():
if "build" in obj:
with open(obj["build"]["context"] + "/Dockerfile", "r") as df:
targets = []
new_lines = []
for line in df.read().split("\n"):
new_lines.append(line)
if line.startswith("FROM"):
split = line.split(" ")
if len(split) == 4:
targets.append(split[3].strip())
if split[1] not in targets:
encountered_stripped_images.append(
split[1].split("@")[0].strip()
)
split[1] = get_new_image(split[1])
new_lines[-1] = " ".join(split)
if not pull:
with open(obj["build"]["context"] + "/Dockerfile", "w") as df:
df.write("\n".join(new_lines))
elif "image" in obj:
encountered_stripped_images.append(obj["image"].split("@")[0])
yaml_str = yaml_str.replace(
"image: " + obj["image"] + "\n",
"image: " + get_new_image(obj["image"]) + "\n",
)
if not pull:
with open(dc_file, "w") as f:
f.write(yaml_str)
else:
for stripped_image in set(encountered_stripped_images):
print("Pulling ", stripped_image)
subprocess.run(["docker", "pull", stripped_image])
break
else:
print("No docker-compose file found")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment