feat: replace shell script with python implementation
- add requirements.txt - allow disabling a stack - update config example - update README
This commit is contained in:
parent
77a3ae1ed6
commit
ef0620ee41
6 changed files with 92 additions and 49 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1 @@
|
||||||
stacklist
|
config.yml
|
||||||
|
|
30
README.md
30
README.md
|
@ -1,19 +1,27 @@
|
||||||
# Docker Compose Ops (dc-ops)
|
# Docker Compose GitOps (dc-ops)
|
||||||
|
|
||||||
`dc-ops` is a shell script designed to operations automate the local building and updating of several Docker services.
|
`dc-ops` is a simple python script that automates the update and deployment of multiple Docker Compose applications.
|
||||||
It aims to simplify continuous delivery (CD) processes and infrastructure management in a Docker environment.
|
|
||||||
|
|
||||||
## How does it work?
|
## How does it work?
|
||||||
|
It reads a list of git repositories from a YAML configuration file (`config.yml`), pulls the latest changes for each repo, and runs `docker compose up` for each specified `docker-compose.yml` file.
|
||||||
|
|
||||||
* The script iterates through each line of the `stacklist` file (skipping comments) or through all cli parameters
|
## Features
|
||||||
* It fetches the latest changes from the remote git repository and checks if there are new commits
|
- Automatically checks for updates in git repositories
|
||||||
* If there is a change it pulls the updates from the remote git repository, otherwise the entry is skipped
|
- Supports multiple Docker Compose files per repository
|
||||||
* Following this, it runs `docker compose up` which builds, (re)creates and starts the containers
|
- Can be configured to skip certain repositories
|
||||||
|
|
||||||
The `stacklist` file can list either a directory containing a `docker-compose.yml` file or a precise `docker-compose` file (see `stacklist.example`).
|
## Requirements
|
||||||
Alternatively, the stacklist can also be passed as a list of parameters.
|
See `requirements.txt`
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
1. Install the required Python packages: `pip install -r requirements.txt`
|
||||||
|
2. Adapt `config.yml.example` to your needs and save it as `config.yml`
|
||||||
|
3. Run `dc-ops` from the shell or from a cron job
|
||||||
|
|
||||||
* `./dc-ops /path/to/repo /path/to/another-repo /path/with/docker-compose-dev.yml` or
|
## Detailed process
|
||||||
* Update `stacklist` file and run `./dc-ops` (or create a crontab entry)
|
For each enabled stack in the config file, the following process will be executed:
|
||||||
|
|
||||||
|
1. Checking directory existence
|
||||||
|
2. Fetching latest changes from remote repository
|
||||||
|
3. If there is a new commit, it will pull the changes
|
||||||
|
4. Running Docker Compose with the defined or default compose-file(s)
|
||||||
|
|
11
config.yml.example
Normal file
11
config.yml.example
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
stacks:
|
||||||
|
- dir: /path/to/first-repo
|
||||||
|
- dir: /path/to/second-repo
|
||||||
|
compose-files:
|
||||||
|
- docker-compose-dev.yml
|
||||||
|
- dir: /path/to/third-repo
|
||||||
|
compose-files:
|
||||||
|
- subdirA/docker-compose.yml
|
||||||
|
- subdirB/docker-compose.yml
|
||||||
|
- dir: /path/to/fourth-repo
|
||||||
|
enabled: false
|
92
dc-ops
92
dc-ops
|
@ -1,44 +1,70 @@
|
||||||
#! /bin/bash
|
#!/usr/bin/env python3
|
||||||
|
# -*- encoding: utf-8; py-indent-offset: 4 -*-
|
||||||
|
|
||||||
## Author: Sebastian Mark
|
# Author: Sebastian Mark
|
||||||
## CC-BY-SA (https://creativecommons.org/licenses/by-sa/4.0/deed.de)
|
# CC-BY-SA (https://creativecommons.org/licenses/by-sa/4.0/deed.de)
|
||||||
## for civil use only
|
# for civil use only
|
||||||
|
|
||||||
## see README.md
|
import logging as log
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
h1() { echo "* $*"; }
|
import yaml
|
||||||
msg() { echo "$*"; }
|
from git import GitCommandError, NoSuchPathError, Repo
|
||||||
|
|
||||||
BASEDIR=$(dirname "$0")
|
# read config file
|
||||||
|
configfile = Path(__file__).with_name("config.yml")
|
||||||
|
with configfile.open("r") as f:
|
||||||
|
cfg = yaml.safe_load(f.read())
|
||||||
|
|
||||||
# use list from file or passed parameters
|
# init logging
|
||||||
STACKLIST=$(cat "$BASEDIR/stacklist" 2>/dev/null)
|
log.basicConfig(format="%(message)s", level=log.INFO)
|
||||||
[[ $# -gt 0 ]] && STACKLIST=$*
|
|
||||||
|
|
||||||
# iterate list
|
# iterate all stacks
|
||||||
for LINE in $STACKLIST; do
|
for stack in cfg["stacks"]:
|
||||||
grep -q "^[[:space:]]*#" <<<"$LINE" && continue # skip comments
|
# skip disabled stacks
|
||||||
|
if "enabled" in stack and not stack["enabled"]:
|
||||||
|
continue
|
||||||
|
|
||||||
# determine if passed a directory or a file
|
# header
|
||||||
STACKDIR=$LINE
|
stackdir = stack["dir"]
|
||||||
if [[ -f $LINE ]]; then
|
log.info(f"* processing {stackdir}")
|
||||||
STACKDIR=$(dirname "$LINE")
|
|
||||||
COMPOSEFILE=$LINE
|
|
||||||
fi
|
|
||||||
|
|
||||||
h1 "processing $STACKDIR"
|
# create repo instance if it exists
|
||||||
|
try:
|
||||||
|
repo = Repo(stackdir)
|
||||||
|
except NoSuchPathError:
|
||||||
|
log.error("directory not found")
|
||||||
|
continue
|
||||||
|
|
||||||
# skip if directroy not found
|
# try to fetch latest changes
|
||||||
cd "$STACKDIR" || continue
|
try:
|
||||||
|
repo.git.fetch()
|
||||||
|
except GitCommandError as e:
|
||||||
|
log.error(str(e))
|
||||||
|
continue
|
||||||
|
|
||||||
# fetch from repo and check for new commits
|
# check for new commits
|
||||||
git fetch --quiet || continue
|
if repo.rev_parse("HEAD") == repo.rev_parse(f"origin/{repo.active_branch}"):
|
||||||
if [[ $(git rev-parse HEAD) == $(git rev-parse "@{u}") ]]; then
|
log.info("no changes - skipping")
|
||||||
msg "no changes - skipping"
|
continue
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# pull new commits and run docker compose
|
# pull remote changes to local branch
|
||||||
git pull || continue
|
try:
|
||||||
docker compose --file "${COMPOSEFILE:=docker-compose.yml}" up --build --detach --remove-orphans
|
log.info(repo.git.pull())
|
||||||
done
|
except GitCommandError as e:
|
||||||
|
log.error(str(e))
|
||||||
|
continue
|
||||||
|
|
||||||
|
# run `docker compose` for all compose-files
|
||||||
|
# (or just for the directory if no compose-file defined)
|
||||||
|
composefiles = stack.get("compose-files", ["docker-compose.yml"])
|
||||||
|
for composefile in composefiles:
|
||||||
|
try:
|
||||||
|
subprocess.run(
|
||||||
|
f"docker compose --file {stackdir}/{composefile} up --build --detach --remove-orphans", # noqa
|
||||||
|
shell=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
pass
|
||||||
|
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
gitpython
|
||||||
|
pyyaml
|
|
@ -1,4 +0,0 @@
|
||||||
/path/to/repo
|
|
||||||
/path/to/another-repo
|
|
||||||
/path/with/docker-compose.yml
|
|
||||||
/another-path/with/docker-compose-dev.yml
|
|
Loading…
Reference in a new issue