Skip to content

Commit

Permalink
Merge pull request #174 from ismet55555/build-number-parse
Browse files Browse the repository at this point in the history
feat: Pass Any URL That Includes the Build URL via `--url` Option
  • Loading branch information
ismet55555 committed Dec 1, 2022
2 parents 463ee22 + 820c6c5 commit e92efe7
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .bumpversion.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.0.77
current_version = 0.0.78
commit = True
tag = True
allow_dirty = False
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.0.77
0.0.78
2 changes: 1 addition & 1 deletion docs/source/cli_outline.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Below is a complete outline of the `yojenkins` CLI command structure.

!!! note
As of Version: **0.0.77**
As of Version: **0.0.78**

```text
yojenkins
Expand Down
8 changes: 4 additions & 4 deletions docs/source/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@ The following is the main menu displayed when running `yojenkins --help`.
YOJENKINS (Version: 0.0.00)
yojenkins is a tool that is focused on interfacing with a Jenkins server from
the comfort of the beloved command line. This tool can also be used as a
middleware utility, generating and passing Jenkins information or automating
tasks.
yojenkins is a flexible tool that is focused on interfacing with Jenkins
server from the comfort of the beloved command line. This tool can also be
used as a middleware utility, generating and passing Jenkins information or
automating tasks.
QUICK START:
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import setuptools

# Package version number (Updated via bump2version tool)
__version__ = "0.0.77"
__version__ = "0.0.78"

def check_py_version():
"""Check the python version"""
Expand Down
2 changes: 1 addition & 1 deletion yojenkins/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

__title__ = 'yojenkins'
__summary__ = "A CLI tool to interface with Jenkins server"
__version__ = "0.0.77"
__version__ = "0.0.78"
__author__ = "Ismet Handzic"
__copyright__ = "Copyright 2022 %s" % __author__
2 changes: 1 addition & 1 deletion yojenkins/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
MAIN_HELP_TEXT = f"""
\t\t\t \033[93m YOJENKINS (Version: {__version__}) \033[0m
yojenkins is a tool that is focused on interfacing with
yojenkins is a flexible tool that is focused on interfacing with
Jenkins server from the comfort of the beloved command line.
This tool can also be used as a middleware utility, generating and
passing Jenkins information or automating tasks.
Expand Down
2 changes: 1 addition & 1 deletion yojenkins/cli/cli_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@


def _verify_build_url_get_job_format(build_url: str, job: str) -> bool:
"""Utility function to verify buld url and determine if job is URL or name
"""Utility function to verify build url and determine if job is URL or name
Args:
build_url: Build URL
Expand Down
20 changes: 10 additions & 10 deletions yojenkins/cli_sub_commands/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@cli_decorators.profile
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=bool, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.pass_context
def info(ctx, debug, **kwargs):
Expand All @@ -32,7 +32,7 @@ def info(ctx, debug, **kwargs):
@cli_decorators.profile
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number', metavar='INT')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=bool, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.pass_context
def status(ctx, debug, **kwargs):
Expand All @@ -50,7 +50,7 @@ def status(ctx, debug, **kwargs):
@cli_decorators.profile
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=str, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.pass_context
def abort(ctx, debug, **kwargs):
Expand All @@ -67,7 +67,7 @@ def abort(ctx, debug, **kwargs):
@cli_decorators.profile
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=str, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.pass_context
def delete(ctx, debug, **kwargs):
Expand All @@ -87,7 +87,7 @@ def delete(ctx, debug, **kwargs):
@cli_decorators.list
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=str, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.pass_context
def stages(ctx, debug, **kwargs):
Expand All @@ -104,7 +104,7 @@ def stages(ctx, debug, **kwargs):
@cli_decorators.profile
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=str, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.option('--tail', type=float, required=False, help='Last of logs. If < 1 then %, else number of lines')
@click.option('-dd',
Expand Down Expand Up @@ -145,7 +145,7 @@ def logs(ctx, debug, **kwargs):
@cli_decorators.profile
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=str, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.pass_context
def browser(ctx, debug, **kwargs):
Expand All @@ -163,7 +163,7 @@ def browser(ctx, debug, **kwargs):
@cli_decorators.profile
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=str, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.option('-s', '--sound', type=bool, required=False, is_flag=True, help='Enable sound effects')
@click.pass_context
Expand All @@ -184,7 +184,7 @@ def monitor(ctx, debug, **kwargs):
@cli_decorators.list
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=str, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.pass_context
def parameters(ctx, debug, **kwargs):
Expand All @@ -204,7 +204,7 @@ def parameters(ctx, debug, **kwargs):
@cli_decorators.profile
@click.argument('job', nargs=1, type=str, required=False)
@click.option('-n', '--number', type=int, required=False, help='Build number')
@click.option('-u', '--url', type=str, required=False, help='Build URL (No job info needed)')
@click.option('-u', '--url', type=str, required=False, help='Flexible build URL (No job info needed)')
@click.option('--latest', type=str, required=False, is_flag=True, help='Latest build (Replaces --number)')
@click.option('--follow-logs',
type=bool,
Expand Down
140 changes: 117 additions & 23 deletions yojenkins/utility/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ def translate_kwargs(original_kwargs: Dict[str, Any]) -> Dict[str, Any]:
Details: This is primarirly used if kwarg keys are named like
a module or python native object.
Examples:
- pretty -> opt_pretty
- json -> opt_json
Args:
original_kwargs: Key-value data to be updated
Expand All @@ -81,6 +84,9 @@ def print2(message: str, bold: bool = False, color: str = 'reset') -> None:
- Colors: `black` (might be a gray), `red`, `green`, `yellow` (might be an orange), `blue`,
`magenta`, `cyan`, `white` (might be light gray), `reset` (reset the color code only)
Example Usage:
- `print2("Hey there!", bold=True, color="green")`
Args:
message: Message to print to console
bold : Whether to bold the message
Expand All @@ -90,7 +96,10 @@ def print2(message: str, bold: bool = False, color: str = 'reset') -> None:


def fail_out(message: str) -> None:
"""Output a failure message to the console, then exit
"""Output one failure message to the console, then exit
Example Usage:
- `fail_out("Something went wrong!")`
Args:
message: Message to output to console
Expand All @@ -102,8 +111,11 @@ def fail_out(message: str) -> None:
def failures_out(messages: list) -> None:
"""Output multiple failure messages to the console, then exit
Example Usage:
- `failures_out(["Oh no!", "This is not good!"])`
Args:
message: Messages to output to console
message: Multiple messages to output to console
"""
for message in messages:
echo(style(message, fg='bright_red', bold=True))
Expand Down Expand Up @@ -300,6 +312,9 @@ def is_list_items_in_dict(list_items: list, dict_to_check: dict) -> Union[int, N
list_items : List of items to find in dict_to_check
dict_to_check : Dict to match the top level keys to
Example Usage:
- `is_list_items_in_dict(["hey", "yo"], {"yo": 1}) -> 1`
Returns:
Index of any/first matched item in the list to the top level keys of the dict
"""
Expand All @@ -310,7 +325,7 @@ def is_list_items_in_dict(list_items: list, dict_to_check: dict) -> Union[int, N


def iter_data_empty_item_stripper(iter_data: Union[Dict, List, Set, Tuple]) -> Union[Dict, List, Set, Tuple]:
"""Removes any empty data from a nested or un-nested iter item
"""Removes any empty data from a nested or un-nested iter item (list, dict, set, etc)
Details: https://stackoverflow.com/a/27974027/11294572
Expand All @@ -334,7 +349,7 @@ def iter_data_empty_item_stripper(iter_data: Union[Dict, List, Set, Tuple]) -> U


def is_credential_id_format(text: str) -> bool:
"""Checking if the entire text is in Jenkins credential ID format
"""Checking if the passed text is in Jenkins credential ID format
Args:
text: The text to check
Expand All @@ -352,13 +367,13 @@ def is_credential_id_format(text: str) -> bool:


def is_full_url(url: str) -> bool:
"""TODO Docstring
"""Check if passed string is a valid and full URL
Args:
TODO
url: The text to check
Returns:
TODO
True if valid and full URL, else False
"""

# TODO: Replace this same function in cli_utility.py usages with this one within classes
Expand Down Expand Up @@ -392,11 +407,7 @@ def url_to_name(url: str) -> str:
# name = url_components.path.strip('/').replace('/job/', '/').strip().strip('/').strip('job/').replace('view/', '').replace('change-requests/', '')
url_split = url_components.path.strip().strip('/').split('/')
remove_list = ['job', 'view', 'change-requests']

filtered_list = []
for list_item in url_split: # TODO: Use list comprehension
if list_item not in remove_list:
filtered_list.append(list_item)
filtered_list = [list_item for list_item in url_split if list_item not in remove_list]

# Assemble back
name = '/'.join(filtered_list)
Expand Down Expand Up @@ -507,29 +518,112 @@ def build_url_to_other_url(build_url: str, target_url: str = 'job') -> str:
return result_url


def build_url_to_build_number(build_url: str) -> int:
"""Get the build number from its build URL
def build_url_to_build_number(build_url: str) -> Union[int, None]:
"""If possible, extract the build number from a passed Jenkins URL
In order to extract the build number, the passed URL must be related to the
build (ie. URL of build logs)
Args:
build_url : The URL of the build
build_url : URL of anything build related
Example Usage:
- `build_url_to_build_number("http://......job/myJob/15/") -> 15`
- `build_url_to_build_number("http://......job/myJob/15/console") -> 15`
- `build_url_to_build_number("http://......job/myJob") -> None`
Returns:
The build number
The build number if extracted, else None
"""
logger.debug(f'Extracting build number from URL: {build_url} ...')
# Dissect the build url
url_parsed = urlparse(build_url)
# Split the URL path, remove the empty items
url_path_split_list = list(filter(None, urlparse(build_url).path.split('/')))

# Split the path, remove the empty items
url_path_split_list = list(filter(None, url_parsed.path.split('/')))

# Get the last item, convert to int
build_number = int(url_path_split_list[-1])
# Stepping from the back one item at a time
build_number = None
for index in range(len(url_path_split_list) - 1, 2, -1):
try:
build_number = int(url_path_split_list[index])
if url_path_split_list[index - 2] != "job":
raise ValueError
break
except ValueError:
build_number = None

logger.debug(f'From build URL "{build_url}" extracted build number "{build_number}"')
return build_number


def is_complete_build_url(build_url: str) -> bool:
"""Check if passed URL is a complete and valid build URL
Args:
build_url : URL of anything build related
Returns:
True if valid and complete, else False
"""
if not build_url:
return False
url_path_split_list = list(filter(None, urlparse(build_url).path.split('/')))
is_complete = True
try:
int(url_path_split_list[-1])
if url_path_split_list[-3] != "job":
is_complete = False
except ValueError:
is_complete = False
return is_complete


def build_url_complete(build_url: str) -> Union[str, None]:
"""If possible, extract only the build URL from a passed Jenkins URL
In order to extract the complete build URL, the passed URL must be related to the
build (ie. URL of build logs)
Args:
build_url : URL of anything build related
Example Usage:
- `build_url_complete("......job/myJob/15/")` -> `......job/myJob/15/``
- `build_url_complete("......job/myJob/15/console")` -> `......job/myJob/15/`
- `build_url_complete("......job/myJob/")` -> `None`
Returns:
Cleaned build URL, else None
"""
if not build_url:
return None

if is_complete_build_url(build_url):
return build_url

# Split the URL path, remove the empty items
url_parsed = urlparse(build_url)
url_path_split_list = list(filter(None, url_parsed.path.split('/')))

# Stepping from the back one item at a time
for index in range(len(url_path_split_list.copy()) - 1, 0, -1):
try:
int(url_path_split_list[index])
if url_path_split_list[index - 2] != "job":
raise ValueError
break
except ValueError:
url_path_split_list.pop()

if len(url_path_split_list) < 2:
logger.debug(f'Cannot extract complete build url from "{build_url}"')
return None

# Assemble Everything back
path_new = '/'.join(url_path_split_list)
base_url = url_parsed.scheme + '://' + url_parsed.netloc
build_url_complete = urljoin(base_url, path_new) + '/'

logger.debug(f'Extracted complete build URL "{build_url_complete}" from "{build_url}"')
return build_url_complete


def item_url_to_server_url(url: str, include_scheme: bool = True) -> str:
"""From build_url get job or folder url
Expand Down

0 comments on commit e92efe7

Please sign in to comment.