Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update-dockerfile-update-workflow #19147

Merged
merged 36 commits into from
Aug 20, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
107ebaf
update-dockerfile-update-workflow
sapirshuker Aug 14, 2023
2f641c4
remove use with version
sapirshuker Aug 14, 2023
decdc95
add logs, change branch name,resolve githubaction trigger
sapirshuker Aug 14, 2023
50aa4c0
change branch name, added logs
sapirshuker Aug 14, 2023
d4f5ed3
fixes, add logs
sapirshuker Aug 14, 2023
38f0ae3
CR fixes, change branch name, fix workflow
sapirshuker Aug 14, 2023
13ecc49
CR fixes, change branch name, fix workflow
sapirshuker Aug 14, 2023
f3bee21
update regex, change branch name
sapirshuker Aug 14, 2023
631a308
update regex, change branch name
sapirshuker Aug 14, 2023
d0af773
fixes, change branch name
sapirshuker Aug 15, 2023
2373817
fixes, change branch name
sapirshuker Aug 15, 2023
9d0edd9
fixes, change branch name
sapirshuker Aug 15, 2023
ed20c37
for testing
sapirshuker Aug 15, 2023
28fc33a
fixes
sapirshuker Aug 15, 2023
447b4e7
fixes, change branch name
sapirshuker Aug 15, 2023
0efcff5
fixes, change branch name
sapirshuker Aug 15, 2023
8552908
change poetry install, change branch name, change log output
sapirshuker Aug 15, 2023
19edd58
fixes
sapirshuker Aug 15, 2023
bd677d0
change python version
sapirshuker Aug 15, 2023
a588294
change python version
sapirshuker Aug 15, 2023
5ccdca1
change python version
sapirshuker Aug 16, 2023
8b5a69a
update pipfile python version, change branch name
sapirshuker Aug 16, 2023
986cfaf
change python version, add logs,remove of for test
sapirshuker Aug 16, 2023
b3aa47e
Merge branch 'master' into update-dockerfile-update-workflow
sapirshuker Aug 16, 2023
9bc1f94
change branch name
sapirshuker Aug 16, 2023
7c9f464
change branch name, remove reverted content, CR fixes
sapirshuker Aug 16, 2023
b60ea75
check workflow
sapirshuker Aug 16, 2023
1caf49b
revert workflow changes
sapirshuker Aug 16, 2023
09fa8be
check workflow
sapirshuker Aug 16, 2023
000728c
revert workflow changes
sapirshuker Aug 16, 2023
e689514
revert all needed to be reverted
sapirshuker Aug 17, 2023
42c53f4
revert all needed to be reverted
sapirshuker Aug 17, 2023
24f065f
fixes CR review and last check
sapirshuker Aug 17, 2023
6cff664
fixes CR review and last check
sapirshuker Aug 17, 2023
fc3c780
revert changes fot tests
sapirshuker Aug 17, 2023
1959ccf
CR fixes
sapirshuker Aug 20, 2023
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
25 changes: 13 additions & 12 deletions .github/workflows/create-update-dockerfiles-PR.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ name: Create update dockerfiles PR

on:
workflow_run:
workflows: [Update external base images, Update internal base images]
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
types: completed
workflows: [Update external base images test, Update internal base images test]
types:
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
- completed
workflow_dispatch:

jobs:
Expand All @@ -22,7 +23,7 @@ jobs:
GITHUB_TOKEN: ${{secrets.CONTENT_BOT_TOKEN}}
run: |
git fetch
branches=$(git branch -r | grep autoupdate/)
branches=$(git branch -r | grep TESTautoTESTupdate/)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reminder to revert

matrix="[ "
for branch in $(echo $branches); do
exists=`gh pr list --state open -H ${branch#origin/}`
Expand Down Expand Up @@ -55,13 +56,13 @@ jobs:
destination_branch: "" # If blank, default: master
pr_title: "build(dockerfiles) update Dockerfile - ${{ matrix.branches }}" # Title of pull request
pr_body: "This is automated PR to update dockerfiles base images\n${{ matrix.branches }}"
- name: approve and merge
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_URL: ${{ steps.open-pr.outputs.pr_url }}
if: ${{ steps.open-pr.outputs.pr_url }}
run: |
echo "Approving and merging"
gh pr review --approve "$PR_URL"
gh pr merge --auto --squash "$PR_URL"
# - name: approve and merge
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reminder to revert

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert

# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# PR_URL: ${{ steps.open-pr.outputs.pr_url }}
# if: ${{ steps.open-pr.outputs.pr_url }}
# run: |
# echo "Approving and merging"
# gh pr review --approve "$PR_URL"
# gh pr merge --auto --squash "$PR_URL"

14 changes: 9 additions & 5 deletions .github/workflows/update-external-base-images.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
name: Update external base images
name: Update external base images test

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revert

on:
schedule:
- cron: '0 0 * * *'
push:
branches:
- update-dockerfile-update-workflow
# schedule:
# - cron: '0 0 * * *'
workflow_dispatch:


jobs:
update-docker-files:
Expand All @@ -24,7 +27,8 @@ jobs:
git config --global user.name "auto dockerfiles update"
echo "==== $(date): Starting pipenv setup... ===="
python -m pip install --upgrade pip
pip install pipenv
pip install pipenv==2023.3.18
pip install poetry
pipenv install
echo "==== Finished ===="
[[ ${{ vars.DISABLE_TIMESTAMP_AUTOUPDATES }} = 'true' ]] && tu_flag="--no-timestamp-updates"
Expand Down
13 changes: 9 additions & 4 deletions .github/workflows/update-internal-base-images.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
name: Update internal base images
name: Update internal base images test

# on:
# schedule:
# - cron: '30 0,4,8,12,16,20 * * *'
on:
schedule:
- cron: '30 0,4,8,12,16,20 * * *'
push:
branches:
- update-dockerfile-update-workflow
workflow_dispatch:

jobs:
Expand All @@ -22,7 +26,8 @@ jobs:
git config --global user.name "auto dockerfiles update"
echo "==== $(date): Starting pipenv setup... ===="
python -m pip install --upgrade pip
pip install pipenv
pip install pipenv==2023.3.18
pip install poetry
pipenv install
echo "==== Finished ===="
pipenv run python ./utils/auto_dockerfile_update/update_dockerfiles.py -t internal
Expand Down
32 changes: 29 additions & 3 deletions utils/auto_dockerfile_update/get_dockerfiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
import re
from datetime import datetime
from pathlib import Path
from typing import List, Dict
from typing import List, Dict, Tuple
from configparser import ConfigParser, MissingSectionHeaderError
from dateutil.parser import parse


EXTRACT_PYTHON_VERSION_REGEX = re.compile(r"\d{1,3}.\d{1,3}.\d{1,3}")
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
BASE_IMAGE_REGEX = re.compile(r"(?:FROM [\S]+)")
INTERNAL_BASE_IMAGES = re.compile(r"(demisto\/|devdemisto\/)")
LAST_MODIFIED_REGEX = re.compile(r"# Last modified: [^\n]*")



def get_last_modified(docker_file_content: str) -> str:
"""
Get the last modified section in dockerfile, if not exists return 1.1.2000 12:00:00 +00:00
Expand Down Expand Up @@ -90,7 +91,32 @@ def filter_ignored_files(files_list):
print(f'could not read ignored config {str(e)}')
return files_list


def get_file_path_and_docker_version_if_exist(dockerfile: dict, latest_tag:str)-> Tuple:
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
"""Gets the Pipfile or the pyproject.toml file path
and the docker version from the Dockerfile.
Args:
dockerfile (str): A dict that represents the docker file.
latest_tag (str): latest tag string.
Returns:
Tuple[str,str,bool]: The file path, The docker file tag, if exists.
"""
base_path = dockerfile["path"]
base_path=base_path.replace("/Dockerfile", "")
pipfile_path = glob(f"{base_path}/Pipfile", recursive=True)
pyproject_path = glob(f"{base_path}/pyproject.toml", recursive=True)
extracted_tag_search = re.search(EXTRACT_PYTHON_VERSION_REGEX, latest_tag)
if extracted_tag_search:
extracted_tag = extracted_tag_search.group(0)
if pipfile_path:
return pipfile_path[0],extracted_tag,True
elif pyproject_path:
return pyproject_path[0],extracted_tag,True
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
else:
print(f"[ERROR] Can't find Pipfile/pyproject file for {dockerfile['name']}.")
else:
print(f"[ERROR] Can't find docker tag for {dockerfile['name']}.")
return base_path,extracted_tag,False
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved

def get_docker_files(base_path="docker/", devonly=False, external=False, internal=False) -> List[Dict]:
"""
Get all the relevant dockerfiles from the repository.
Expand Down
141 changes: 131 additions & 10 deletions utils/auto_dockerfile_update/update_dockerfiles.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import os

import argparse

from get_dockerfiles import get_docker_files
from get_dockerfiles import get_docker_files,get_file_path_and_docker_version_if_exist
from get_latest_tag import get_latest_tag, parse_versions
from typing import Dict, List
from typing import Dict, List,Tuple
import dateutil
from git import Repo, GitCommandError
import re
from get_dockerfiles import LAST_MODIFIED_REGEX
from datetime import datetime, timezone
from functools import reduce
import subprocess

BATCH_SIZE = 1

PYTHON_VERSION_PIPFILE_REGEX = re.compile(r"python_version = \"([^\"]+)\"")
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
PYTHON_VERSION_PYPROJECT_REGEX = re.compile(r"python = \"([^\"]+)\"")

def is_docker_file_outdated(dockerfile: Dict, latest_tag: str, last_updated: str = "", no_timestamp_updates=True) -> bool:
"""
Expand All @@ -40,13 +40,130 @@ def is_docker_file_outdated(dockerfile: Dict, latest_tag: str, last_updated: str

return False

def extract_current_python_version(file_path: str)->Tuple[List,str,bool]:
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
"""Extract the current python version from Pipfile or the pyproject.toml.

Args:
file_path (str): The file path to the Pipfile or the pyproject.toml file.

Returns:
Tuple[List,str, bool]: The python version in list and str.
"""
python_version = ""
try:
with open(file_path, "r") as f:
file_content = f.read()
if "Pipfile" in file_path:
python_version = re.search(PYTHON_VERSION_PIPFILE_REGEX, file_content)
elif "pyproject.toml" in file_path:
python_version = re.search(PYTHON_VERSION_PYPROJECT_REGEX, file_content)
except Exception as e:
print(f"{e}: Can't read file {file_path}")
if python_version:
full_str_python_version = python_version.group(0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats the difference? Why do we need both of these

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The full_str_python_version is "python_version= "3.9"" format, we need it in order to replace it in the pipfile/pyproject file.
The string_version_number is only the version 3.9

string_version_number=python_version.group(1)
return string_version_number,full_str_python_version,False
else:
print(f"can't extract python version form:{file_path}")
return "","",False

def replace_python_version(file_path: str, version: List,
full_str_python_version:str)->bool:
"""Replace the current python version in the Pipfile or the pyproject.toml.

Args:
file_path (str): The file path to the Pipfile or the pyproject.toml file.
version (List): The updated version.
full_str_python_version (str): The older version.
"""
to_replace = False
with open(file_path, "r") as f:
file_content = f.read()
if "Pipfile" in file_path:
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
if full_str_python_version != f"python_version = \"{version[0]}.{version[1]}\"":
file_content=file_content.replace(full_str_python_version,f"python_version = \"{version[0]}.{version[1]}\"")
to_replace = True
elif "pyproject.toml" in file_path:
if full_str_python_version != f"python = \"~{version[0]}.{version[1]}\"":
file_content=file_content.replace(full_str_python_version,f"python = \"~{version[0]}.{version[1]}\"")
to_replace= True
if to_replace:
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
with open(file_path, "w") as f:
f.write(file_content)
return True
return False

def change_pyproject_or_pipfile(file_path, str_version) -> Tuple[bool,str]:
"""Replace the current python version in the Pipfile or the pyproject.toml.

Args:
file_path (str): The file path to the Pipfile or the pyproject.toml file.
version (List): The updated version.
full_str_python_version (str): The older version.
"""
version = str_version.split(".")
current_version, full_str_python_version, success_extraction=extract_current_python_version(file_path)
if success_extraction:
result=replace_python_version(file_path, version, full_str_python_version)
return result,current_version
return False, []
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved

def run_lock(base_path_docker:str,pipfile_or_pyproject_path:str)->bool:
"""Runs poetry lock --no-update or pipfile lock --keep-outdated.

Args:
base_path_docker (str): The DockerFile path.
pipfile_or_pyproject_path (str): The file path to the Pipfile or the pyproject.toml file.
"""
base_path=base_path_docker.replace("/Dockerfile", "")
current_directory = os.getcwd()
os.chdir(f"{current_directory}/" + base_path)
try:
if "Pipfile" in pipfile_or_pyproject_path:
# waits for the process to end.
completed_process = subprocess.run(["pipenv", "lock", "--keep-outdated"], check=True)
if completed_process.returncode != 0 :
return False
elif "pyproject.toml" in pipfile_or_pyproject_path:
completed_process = subprocess.run(["poetry", "lock", "--no-update"], check=True)
if completed_process.returncode != 0 :
return False
except subprocess.CalledProcessError as e:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

collapese these catch blocks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need this block in order to get the stderr from the CalledProcessError (It creates a object with the data)

print(f"[ERROR] Lock failed with error: {e}")
except TimeoutError as e:
print(f"[ERROR] Got time out error: {e} for {base_path_docker}")
except Exception as e:
print(f"[ERROR] {e}: for {base_path_docker}")
os.chdir(current_directory)
return True

def update_python_version_pyproject_or_pipfile(dockerfile: Dict, latest_tag: str)-> None:
"""
Updating dockerfile content with the latest tag
Args:
dockerfile (Dict): Dockerfile dict.
latest_tag (str): latest tag string.
Returns:
None
"""
update_result = False
image_name = dockerfile['image_name']
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image_name is the base image?

Copy link
Contributor Author

@sapirshuker sapirshuker Aug 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it is the name of the directory.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is the case we will only update python. we want to update every image based on python as well

Copy link
Contributor Author

@sapirshuker sapirshuker Aug 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sorry it is a mistake this is the name of the base image- can be "alpine", "debian", "python", "powershell",
"python3-deb", "python3", "powershell-ubuntu", "crypto", "python3-ubi", "btfl-soup" etc...

if "python" in image_name:
sapirshuker marked this conversation as resolved.
Show resolved Hide resolved
path,tag, is_exist=get_file_path_and_docker_version_if_exist(dockerfile,latest_tag)
if is_exist:
update_result, old_version = change_pyproject_or_pipfile(path,tag)
if update_result:
lock_result=run_lock(dockerfile['path'],path)
if not lock_result:
print(f"[ERROR] Got Error with lock for: {image_name} revert pipfile/pyproject.toml changes")
change_pyproject_or_pipfile(path,old_version)

def update_dockerfile(dockerfile: Dict, latest_tag: str) -> None:
"""
Updating dockerfile content with the latest tag
Args:
dockerfile (Dict): Dockerfile dict
latest_tag (str): latest tag string
dockerfile (Dict): Dockerfile dict.
latest_tag (str): latest tag string.
Returns:
None
"""
Expand Down Expand Up @@ -86,9 +203,10 @@ def update_external_base_dockerfiles(git_repo: Repo, no_timestamp_updates=True)
latest_tag_last_updated = latest_tag.get('last_updated', '')

if is_docker_file_outdated(file, latest_tag_name, latest_tag_last_updated, no_timestamp_updates):
branch_name = fr"autoupdate/Update_{file['repo']}_{file['image_name']}_from_{file['tag']}_to_{latest_tag_name}"
branch_name = fr"TESTautoTESTupdate/Update_{file['repo']}_{file['image_name']}_from_{file['tag']}_to_{latest_tag_name}"
update_and_push_dockerfiles(git_repo, branch_name, [file], latest_tag_name)
print(f"Updated {file['path']}")
print("Finished to update dockerfiles")


def create_dependencies_json(all_docker_files: List[Dict]) -> Dict:
Expand Down Expand Up @@ -133,8 +251,9 @@ def update_internal_base_dockerfile(git_repo: Repo) -> None:
is_docker_file_outdated(file, latest_tag_name, latest_tag_last_updated)]
for batch_slice in batch(outdated_files, BATCH_SIZE):
image_names = reduce(lambda a, b: f"{a}-{b}", [file['name'] for file in batch_slice])
branch_name = fr"autoupdate/{base_image}_{image_names}_{latest_tag_name}"
branch_name = fr"TESTautoTESTupdate/{base_image}_{image_names}_{latest_tag_name}"
update_and_push_dockerfiles(git_repo, branch_name, batch_slice, latest_tag_name)
print("Finished to update dockerfiles")


def update_and_push_dockerfiles(git_repo: Repo, branch_name: str, files: List[Dict], latest_tag_name: str) -> None:
Expand All @@ -152,15 +271,17 @@ def update_and_push_dockerfiles(git_repo: Repo, branch_name: str, files: List[Di
print(f"Trying to create new branch: {branch_name}")
original_branch = git_repo.active_branch
if branch_name in git_repo.git.branch("--all"):
print("Branch already exits.")
print("Branch already exists.")
return
try:
branch = git_repo.create_head(branch_name)
branch.checkout()

for file in files:
update_python_version_pyproject_or_pipfile(file, latest_tag_name)
update_dockerfile(file, latest_tag_name)

print(git_repo.index.diff(git_repo.head.commit))
git_repo.git.add("*")
git_repo.git.commit(m=f"Update Dockerfiles")
git_repo.git.push('--set-upstream', 'origin', branch)
Expand Down
Loading