Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/publish-on-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Publish Docker Images on Release

on:
release:
types: [published]
workflow_dispatch:
inputs:
release_tag:
description: 'Release tag to use (e.g., v1.2.3). If empty, uses release tag from the release event.'
required: false
default: 'v0.0.1'

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v2

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME_AL }}
password: ${{ secrets.DOCKERHUB_API_KEY_AL }}

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install docker python-dotenv requests rich

- name: Run workflow_runner.py
env:
RELEASE_TAG: ${{ github.event.inputs.release_tag || github.ref_name }}
DOCKERHUB_USERNAME_AL: ${{ secrets.DOCKERHUB_USERNAME_AL }}
DOCKERHUB_API_KEY_AL: ${{ secrets.DOCKERHUB_API_KEY_AL }}
MAILJET_API_KEY: ${{ secrets.MAILJET_API_KEY }}
MAILJET_API_SECRET: ${{ secrets.MAILJET_API_SECRET }}
run: |
python workflow_runner.py
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ test_system_runner.json
recorder_*
local_feeder
build
.vscode/
.vscode/
.env
1 change: 1 addition & 0 deletions LocalFeeder/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
FROM python:3.10.6-slim-bullseye
LABEL org.opencontainers.image.authors="Joseph McKinsey <Joseph.McKinsey@nrel.gov>"
RUN apt-get update
RUN apt-get install -y git ssh
RUN mkdir LocalFeeder
Expand Down
7 changes: 7 additions & 0 deletions broker/oedisi.code-workspace
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"folders": [
{
"path": ".."
}
]
}
1 change: 1 addition & 0 deletions recorder/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
FROM python:3.10.6-slim-bullseye
LABEL org.opencontainers.image.authors="Aadil Latif <Aadil.Latif@nrel.gov>"
RUN apt-get update
RUN apt-get install -y git ssh
RUN mkdir Recorder
Expand Down
157 changes: 157 additions & 0 deletions workflow_runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
from requests.auth import HTTPBasicAuth
from collections import defaultdict
from pathlib import Path
import requests
import json
import os

from dotenv import load_dotenv
import requests
from rich import print
import docker


load_dotenv()

# --- Configuration ---
DOCKERHUB_USERNAME_AL = os.getenv("DOCKERHUB_USERNAME_AL")
DOCKERHUB_API_KEY_AL = os.getenv("DOCKERHUB_API_KEY_AL")
MAILJET_API_KEY = os.environ["MAILJET_API_KEY"]
MAILJET_API_SECRET = os.environ["MAILJET_API_SECRET"]
TAG = os.environ["RELEASE_TAG"]


def collect_components():
# Simulate collecting components
print("Collecting components...")
components = json.load(open("components.json", "r")) # Load components from a JSON file
for component_name, component_path in components.items():
try:
print(f"Building image for component {component_name}...")
component_path = Path(component_path).parent
if not component_path.is_dir():
raise ValueError(f"Component path {component_path} is not a directory")
if not (component_path / "Dockerfile").is_file():
raise ValueError(f"No Dockerfile found in {component_path}")
build_and_push_docker_image(component_name, component_path, )
except Exception as e:
print(f"An unexpected error occurred: {e}")

def build_and_push_docker_image(image_name, docker_file_path, tag="v0.0.1"):
client = docker.from_env()
REPOSITORY_NAME = f"{DOCKERHUB_USERNAME_AL}/{image_name}:{tag}".lower()
email_msg = f"Building Docker image: {REPOSITORY_NAME} from directory {docker_file_path}"

try:
# Build the image
image, build_logs = client.images.build(
path=str(docker_file_path),
tag=REPOSITORY_NAME,
rm=True, # Remove intermediate containers after a successful build
nocache=False, # Do not use cache when building the image
)

email_msg += f"\nSuccessfully built image: {REPOSITORY_NAME}"

labels = image.attrs['Config'].get('Labels') or {}
author_names = []
author_emails = []
authors_label = labels.get("org.opencontainers.image.authors")
if authors_label:
for author in authors_label.split(","):
author = author.strip()
if "<" in author and ">" in author:
name, email = author.split("<", 1)
author_names.append(name.strip())
author_emails.append(email.strip("> ").strip())
else:
author_names.append(author)
author_emails.append(None)

print(f"Image authors: {{'names': author_names, 'emails': author_emails}}")


except docker.errors.BuildError as e:
email_msg += f"\nError building image: {e}"
print(f"Error building image: {e}")
return

print(f"2. Logging in to Docker Hub as {DOCKERHUB_USERNAME_AL}")
email_msg += f"\nLogging in to Docker Hub as {DOCKERHUB_USERNAME_AL}"

email_msg += f"\nPushing image to Docker Hub: {REPOSITORY_NAME}"
print(f"3. Pushing image to Docker Hub: {REPOSITORY_NAME}")
try:
# Push the image to Docker Hub
# The push operation returns an iterator of events
push_logs = client.images.push(
repository=f"{DOCKERHUB_USERNAME_AL}/{image_name}".lower(),
tag=TAG,
stream=True,
decode=True
)
for line in push_logs:
if 'status' in line:
print(line['status'])
elif 'error' in line:
print(f"Error during push: {line['error']}")
email_msg += f"\nError during push: {line['error']}"

print("Image pushed successfully to Docker Hub.")
email_msg += f"\nImage pushed successfully to Docker Hub."


except docker.errors.APIError as e:
print(f"Docker API Error during push: {e}")
email_msg += f"Docker API Error during push: {e}"
except Exception as e:
print(f"An unexpected error occurred: {e}")
email_msg += f"An unexpected error occurred: {e}"

for name, email in zip(author_names, author_emails):
if not email:
print(f"Skipping author '{name}' - no email available")
continue

payload = {
"Messages": [
{
"From": {
"Email": "aadil.latif@gmail.com",
"Name": "Aadil Latif"
},
"To": [
{
"Email": email,
"Name": name
}
],
"Subject": "OEDISI GitHub Workflow Notification",
"TextPart": email_msg,
}
]
}

try:
response = requests.post(
"https://api.mailjet.com/v3.1/send",
json=payload,
auth=HTTPBasicAuth(MAILJET_API_KEY, MAILJET_API_SECRET),
timeout=30,
)
response.raise_for_status()
print(f"Mail sent to {email}: {response.status_code}")
except requests.exceptions.HTTPError as http_err:
# Surface the server response body to help debugging 400 errors
try:
err_text = response.text
except Exception:
err_text = str(http_err)
print(f"Mailjet HTTP error for {email}: {response.status_code} - {err_text}")
except Exception as e:
print(f"Failed to send mail to {email}: {e}")


if __name__ == "__main__":
os.system("docker login -u {} -p {}".format(DOCKERHUB_USERNAME_AL, DOCKERHUB_API_KEY_AL))
collect_components()
Loading