Skip to content

Commit 5aa8e24

Browse files
committed
[patch] merge to master
2 parents 56563f4 + 1116465 commit 5aa8e24

102 files changed

Lines changed: 6536 additions & 1114 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/docs.yml

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Build Documentation
2+
3+
on:
4+
push:
5+
branches:
6+
- '**'
7+
tags-ignore:
8+
- '**'
9+
release:
10+
types: [ published ]
11+
12+
# Ensure only one build at a time for any branch, cancelling any in-progress builds
13+
concurrency:
14+
group: ${{ github.workflow }}-${{ github.ref }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
deploy-docs:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v4
23+
24+
- name: Set up Python
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: '3.12'
28+
29+
- name: Cache pip packages
30+
uses: actions/cache@v4
31+
with:
32+
path: ~/.cache/pip
33+
key: ${{ runner.os }}-pip-${{ hashFiles('**/setup.py', '**/pyproject.toml') }}
34+
restore-keys: |
35+
${{ runner.os }}-pip-
36+
37+
- name: Install package and doc build dependencies
38+
run: |
39+
python -m pip install --upgrade pip
40+
pip install -e ".[docs]"
41+
42+
- name: Build documentation
43+
run: |
44+
mkdocs build
45+
46+
- name: Deploy
47+
uses: JamesIves/github-pages-deploy-action@4.1.7
48+
if: ${{ github.event_name == 'release' || contains(github.event.head_commit.message, '[doc]') }}
49+
with:
50+
branch: gh-pages
51+
folder: site
52+
53+
# Made with Bob

.github/workflows/python-package.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ jobs:
2626
2727
# 2. Python Package Build
2828
# -------------------------------------------------------------------------------------------
29-
- name: Set up Python 3.11
29+
- name: Set up Python 3.12
3030
uses: actions/setup-python@v5
3131
with:
32-
python-version: 3.11
32+
python-version: 3.12
3333

3434
- name: Build the Python package
3535
env:

.github/workflows/python-release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ jobs:
2525
2626
# 2. Python Package Build
2727
# -------------------------------------------------------------------------------------------
28-
- name: Set up Python 3.11
28+
- name: Set up Python 3.12
2929
uses: actions/setup-python@v5
3030
with:
31-
python-version: 3.11
31+
python-version: 3.12
3232

3333
- name: Build the Python package
3434
env:

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,5 @@ venv/
1919
# Other
2020
kubectl.exe
2121
/build
22+
/.vscode
23+
/site

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ repos:
1313
rev: 0.13.1+ibm.64.dss
1414
hooks:
1515
- id: detect-secrets
16+
additional_dependencies: [boxsdk<4]
1617
args: [--baseline, .secrets.baseline, --use-all-plugins, --fail-on-unaudited]

.secrets.baseline

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"files": "^.secrets.baseline$",
44
"lines": null
55
},
6-
"generated_at": "2025-10-27T10:20:07Z",
6+
"generated_at": "2025-12-25T19:13:06Z",
77
"plugins_used": [
88
{
99
"name": "AWSKeyDetector"
@@ -82,15 +82,53 @@
8282
"hashed_secret": "053f5ed451647be0bbb6f67b80d6726808cad97e",
8383
"is_secret": false,
8484
"is_verified": false,
85-
"line_number": 35,
85+
"line_number": 44,
8686
"type": "Secret Keyword",
8787
"verified_result": null
8888
},
8989
{
9090
"hashed_secret": "4f75456d6c1887d41ed176f7ad3e2cfff3fdfd91",
9191
"is_secret": false,
9292
"is_verified": false,
93-
"line_number": 44,
93+
"line_number": 53,
94+
"type": "Secret Keyword",
95+
"verified_result": null
96+
}
97+
],
98+
"docs/cli/create-initial-users.md": [
99+
{
100+
"hashed_secret": "33f220dd67f717cc949db63e21c90e130a6137da",
101+
"is_secret": false,
102+
"is_verified": false,
103+
"line_number": 126,
104+
"type": "Secret Keyword",
105+
"verified_result": null
106+
}
107+
],
108+
"docs/cli/index.md": [
109+
{
110+
"hashed_secret": "33f220dd67f717cc949db63e21c90e130a6137da",
111+
"is_secret": false,
112+
"is_verified": false,
113+
"line_number": 56,
114+
"type": "Secret Keyword",
115+
"verified_result": null
116+
}
117+
],
118+
"docs/cli/notify-slack.md": [
119+
{
120+
"hashed_secret": "1572c8dd915cf3bdecae817b1cb65847b4e94037",
121+
"is_secret": false,
122+
"is_verified": false,
123+
"line_number": 61,
124+
"type": "Secret Keyword",
125+
"verified_result": null
126+
},
127+
{
128+
"hashed_secret": "2d8b1074eb78b85690ced3d2cc0aed0466f6f652",
129+
"is_secret": false,
130+
"is_verified": false,
131+
"line_number": 207,
94132
"type": "Secret Keyword",
95133
"verified_result": null
96134
}

AGENT_INSTRUCTIONS.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# python-devops AI Coding Instructions
2+
3+
## Project Overview
4+
Python package for IBM Maximo DevOps utilities. Provides command-line tools for deployment automation, database validation, Slack notifications, and user management. Distributed as installable package with `setup.py`/`pyproject.toml` and standalone scripts in `bin/`.
5+
6+
## Architecture
7+
- **bin/**: Executable CLI scripts (entry points)
8+
- `mas-devops-*`: Command-line tools for specific operations
9+
- Scripts import from `src/` package for implementation
10+
- Each script typically wraps a single operational task
11+
- **src/**: Main package source code
12+
- Organized by module/function (import path: `from mas_devops import ...`)
13+
- Core utilities: configuration management, API clients, database handlers
14+
- **test/**: Test suite (pytest structure)
15+
- Mirror `src/` structure in test files
16+
- Run with `pytest` or `make test`
17+
- **build/**: Generated artifacts (don't edit)
18+
- `*.egg-info/`: Package metadata
19+
- `dist/`, `*.whl`: Distribution packages
20+
- **setup.py** / **pyproject.toml**: Package definition
21+
- Entry points defined in setup.py pointing to `bin/` scripts
22+
- Dependencies in both files (must keep in sync)
23+
- Version defined once (check both files)
24+
25+
## Key Patterns
26+
- **CLI Tool Pattern**: `bin/mas-devops-*` scripts are thin wrappers
27+
- Import main logic from `src/mas_devops/`
28+
- Handle argument parsing and error reporting
29+
- Example: `mas-devops-notify-slack` → calls slack notification module
30+
- **Dependency Management**:
31+
- Core dependencies in `setup.py` `install_requires`
32+
- Dev dependencies in `setup.py` `extras_require['dev']`
33+
- `requirements.txt` for pinned versions (reproducible installs)
34+
- Keep all three in sync
35+
- **Entry Points**: `setup.py` defines CLI commands
36+
- Format: `'mas-devops-task-name = mas_devops.module:main_function'`
37+
- Creates executable scripts in `bin/` when package installed
38+
- **Module Organization**: Import directly from package
39+
- `from mas_devops.db2_validator import validate_config`
40+
- Avoid deep nesting; keep public API clear
41+
42+
## Development Workflow
43+
```bash
44+
make install # Install package in dev mode (pip install -e .)
45+
make test # Run pytest suite
46+
make test-verbose # Pytest with verbose output
47+
make build # Build distribution (wheel/tarball)
48+
make clean # Remove build artifacts
49+
make all # Clean, test, build
50+
```
51+
52+
## Important Conventions
53+
- **Python Version**: Check `setup.py` for `python_requires` (e.g., `>=3.8`)
54+
- **Entry Points**: Adding new CLI tool requires editing `setup.py` entry_points section
55+
- **Error Handling**: CLI scripts should catch exceptions and exit with meaningful error messages
56+
- **Logging**: Use Python logging module; configure in main module
57+
- **Testing**: Test structure mirrors source; test file for `src/module.py` is `test/test_module.py`
58+
- **Documentation**: Docstrings in functions should describe parameters, return, exceptions
59+
- **Imports**: Use absolute imports from package (`from mas_devops import ...`), not relative
60+
61+
## Common CLI Tools (Reference)
62+
- `mas-devops-create-initial-users-for-saas`: User provisioning (SaaS)
63+
- `mas-devops-db2-validate-config`: Validate DB2 configuration
64+
- `mas-devops-notify-slack`: Send notifications
65+
- `mas-devops-saas-job-cleaner`: Cleanup SaaS jobs
66+
67+
## Integration Points
68+
- **ansible-devops**: Playbooks call these Python utilities for infrastructure setup
69+
- **playbook**: Runbooks document procedures; Python tools automate them
70+
- **Standalone Usage**: Scripts can be called independently or from other tools via entry points
71+
- **Distribution**: Package installed via pip; entry points register CLI commands globally
72+
73+
## When Adding New CLI Tool
74+
1. Create implementation module in `src/mas_devops/`
75+
2. Create script in `bin/mas-devops-tool-name` or update `setup.py` entry_points
76+
3. Add entry to `setup.py` entry_points section
77+
4. Add tests in `test/` matching module structure
78+
5. Update `requirements.txt` if adding dependencies
79+
6. Test with `make install` then run `mas-devops-tool-name --help`
80+
81+
## Packaging & Distribution
82+
- Build: `python -m build` or `make build`
83+
- Outputs: wheel file in `dist/` ready for pip install
84+
- Version managed in `setup.py` (check both setup.py and pyproject.toml)

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
include src/mas/devops/templates/*.json.j2
22
include src/mas/devops/templates/*.yml.j2
33
include src/mas/devops/data/catalogs/*.yaml
4+
include src/mas/devops/data/*.yaml

Makefile

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
.PHONY: install build lint pyinstaller clean
22

3-
venv:
4-
python3 -m venv venv
3+
.venv:
4+
python3 -m venv .venv
55

66
clean:
7-
rm -rf venv
7+
rm -rf .venv
88

9-
install: venv
10-
. venv/bin/activate && python -m pip install --editable .[dev]
9+
install: .venv
10+
. .venv/bin/activate && python -m pip install --editable .[dev]
1111

12-
build: venv
12+
build: .venv
1313
rm -f README.rst
14-
. venv/bin/activate && python -m build
14+
. .venv/bin/activate && python -m build
1515

1616
# Note: "make install" needs to be ran once before this target will work, but we
1717
# don't want to set it as a dependency otherwise it unnecessarily slows down
1818
# fast implement/test cycles. "make install" created an editable install of the
1919
# package which is linked to the files you are editing so there is no need to
2020
# re-install after each change.
2121
unit-test:
22-
. venv/bin/activate && pytest test/src/mock
22+
. .venv/bin/activate && pytest test/src/mock
2323

24-
lint: venv
24+
lint: .venv
2525
rm -f README.rst
26-
. venv/bin/activate && flake8 src --count --select=E9,F63,F7,F82 --show-source --statistics && flake8 src --count --exit-zero --max-complexity=10 --max-line-length=200 --statistics
26+
. .venv/bin/activate && flake8 src --count --select=E9,F63,F7,F82 --show-source --statistics && flake8 src --count --exit-zero --max-complexity=10 --max-line-length=200 --statistics
2727

2828
pyinstaller: venv
2929
rm -f README.rst
30-
. venv/bin/activate && pyinstaller src/mas-upgrade --onefile --noconfirm --add-data="src/mas/devops/templates/ibm-mas-tekton.yaml:mas/devops/templates" --add-data="src/mas/devops/templates/subscription.yml.j2:mas/devops/templates/" --add-data="src/mas/devops/templates/pipelinerun-upgrade.yml.j2:mas/devops/templates/"
30+
. .venv/bin/activate && pyinstaller src/mas-upgrade --onefile --noconfirm --add-data="src/mas/devops/templates/ibm-mas-tekton.yaml:mas/devops/templates" --add-data="src/mas/devops/templates/subscription.yml.j2:mas/devops/templates/" --add-data="src/mas/devops/templates/pipelinerun-upgrade.yml.j2:mas/devops/templates/"

bin/mas-devops-create-initial-users-for-saas

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,18 @@
1010
#
1111
# *****************************************************************************
1212

13+
from mas.devops.users import MASUserUtils
14+
from botocore.exceptions import ClientError
15+
import boto3
16+
import sys
17+
import json
18+
import yaml
1319
from kubernetes import client, config
1420
from kubernetes.config.config_exception import ConfigException
1521
import argparse
1622
import logging
1723
import urllib3
1824
urllib3.disable_warnings()
19-
import yaml
20-
import json
21-
import sys
22-
23-
import boto3
24-
from botocore.exceptions import ClientError
25-
26-
from mas.devops.users import MASUserUtils
27-
2825

2926

3027
if __name__ == "__main__":
@@ -38,7 +35,6 @@ if __name__ == "__main__":
3835
parser.add_argument("--admin-dashboard-port", required=False, default=443)
3936
parser.add_argument("--manage-api-port", required=False, default=443)
4037

41-
4238
group = parser.add_mutually_exclusive_group(required=True)
4339
group.add_argument("--initial-users-yaml-file")
4440
group.add_argument("--initial-users-secret-name")
@@ -66,7 +62,6 @@ if __name__ == "__main__":
6662
admin_dashboard_port = args.admin_dashboard_port
6763
manage_api_port = args.manage_api_port
6864

69-
7065
logger.info("Configuration:")
7166
logger.info("--------------")
7267
logger.info(f"mas_instance_id: {mas_instance_id}")
@@ -88,7 +83,6 @@ if __name__ == "__main__":
8883
config.load_kube_config()
8984
logger.debug("Loaded kubeconfig file")
9085

91-
9286
user_utils = MASUserUtils(mas_instance_id, mas_workspace_id, client.api_client.ApiClient(), coreapi_port=coreapi_port, admin_dashboard_port=admin_dashboard_port, manage_api_port=manage_api_port)
9387

9488
if initial_users_secret_name is not None:
@@ -100,7 +94,7 @@ if __name__ == "__main__":
10094
service_name='secretsmanager',
10195
)
10296
try:
103-
initial_users_secret = aws_sm_client.get_secret_value( # pragma: allowlist secret
97+
initial_users_secret = aws_sm_client.get_secret_value( # pragma: allowlist secret
10498
SecretId=initial_users_secret_name
10599
)
106100
except ClientError as e:
@@ -109,16 +103,15 @@ if __name__ == "__main__":
109103
sys.exit(0)
110104

111105
raise Exception(f"Failed to fetch secret {initial_users_secret_name}: {str(e)}")
112-
106+
113107
secret_json = json.loads(initial_users_secret['SecretString'])
114108
initial_users = user_utils.parse_initial_users_from_aws_secret_json(secret_json)
115109
elif initial_users_yaml_file is not None:
116110
with open(initial_users_yaml_file, 'r') as file:
117111
initial_users = yaml.safe_load(file)
118112
else:
119113
raise Exception("Something unexpected happened")
120-
121-
114+
122115
result = user_utils.create_initial_users_for_saas(initial_users)
123116

124117
# if user details were sourced from an AWS SM secret, remove the completed entries from the secret
@@ -133,14 +126,13 @@ if __name__ == "__main__":
133126
if has_updates:
134127
logger.info(f"Updating secret {initial_users_secret_name}")
135128
try:
136-
aws_sm_client.update_secret( # pragma: allowlist secret
129+
aws_sm_client.update_secret( # pragma: allowlist secret
137130
SecretId=initial_users_secret_name,
138131
SecretString=json.dumps(secret_json)
139132
)
140133
except ClientError as e:
141134
raise Exception(f"Failed to update secret {initial_users_secret_name}: {str(e)}")
142-
143135

144136
if len(result["failed"]) > 0:
145-
failed_user_ids = list(map(lambda u : u["email"], result["failed"]))
146-
raise Exception(f"Sync failed for the following user IDs {failed_user_ids}")
137+
failed_user_ids = list(map(lambda u: u["email"], result["failed"]))
138+
raise Exception(f"Sync failed for the following user IDs {failed_user_ids}")

0 commit comments

Comments
 (0)