Skip to content

Commit c14eb2e

Browse files
committed
STG-4451, STG-4475, STG-4478: dual-mode CLI with microservices installation support
- mdast_cli_core/microservices.py: Clark facade client (Bearer auth, md5-first multipart upload with X-File-Size, application_md5 precheck, fsm_locked two-phase scan creation, scanyon-native (stage, status) polling, /rest/scans/{id}/report) - mdast_cli_core/factory.py: installation mode resolution (MDAST_CLI_MODE env or probe autodetect), monolith path unchanged - mdast_cli/ms_flow.py: microservices scan flow with pre-check gate (exit code 8), engines preflight by type/status, reports to user-provided paths - tests/: 81 tests (unit, contract, smoke incl. monolith regression, security) - CI: tests workflow + test gates before PyPI/Docker publishing
1 parent 85d1177 commit c14eb2e

20 files changed

Lines changed: 1633 additions & 39 deletions
Lines changed: 50 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,50 @@
1-
name: Publish mdast_cli image to Docker Hub
2-
3-
on:
4-
push:
5-
branches:
6-
- main
7-
8-
jobs:
9-
build-n-publish-docker:
10-
name: Docker Hub - Publish mdast_cli
11-
runs-on: ubuntu-22.04
12-
13-
steps:
14-
- uses: actions/checkout@v3
15-
16-
- name: docker login
17-
env:
18-
DOCKER_USER: ${{secrets.DOCKER_USER}}
19-
DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
20-
run:
21-
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
22-
23-
- name: Build the Docker image
24-
25-
run: docker build . --file Dockerfile -t mobilesecurity/mdast_cli:2026.6.1 -t mobilesecurity/mdast_cli:latest
26-
27-
28-
- name: Docker Hub push latest image
29-
run: docker push mobilesecurity/mdast_cli:latest
30-
31-
- name: Docker Hub push tagged image
32-
33-
run: docker push mobilesecurity/mdast_cli:2026.6.1
34-
35-
36-
37-
38-
1+
name: Publish mdast_cli image to Docker Hub
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
jobs:
9+
tests:
10+
name: Tests gate before publish
11+
runs-on: ubuntu-22.04
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-python@v5
15+
with:
16+
python-version: '3.12'
17+
- run: pip install -r requirements.txt -r requirements-dev.txt
18+
- run: pytest -q
19+
20+
build-n-publish-docker:
21+
name: Docker Hub - Publish mdast_cli
22+
needs: tests
23+
runs-on: ubuntu-22.04
24+
25+
steps:
26+
- uses: actions/checkout@v3
27+
28+
- name: docker login
29+
env:
30+
DOCKER_USER: ${{secrets.DOCKER_USER}}
31+
DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
32+
run:
33+
docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
34+
35+
- name: Build the Docker image
36+
37+
run: docker build . --file Dockerfile -t mobilesecurity/mdast_cli:2026.6.1 -t mobilesecurity/mdast_cli:latest
38+
39+
40+
- name: Docker Hub push latest image
41+
run: docker push mobilesecurity/mdast_cli:latest
42+
43+
- name: Docker Hub push tagged image
44+
45+
run: docker push mobilesecurity/mdast_cli:2026.6.1
46+
47+
48+
49+
50+

.github/workflows/python-publish.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,20 @@ on:
66
- main
77

88
jobs:
9+
tests:
10+
name: Tests gate before publish
11+
runs-on: ubuntu-22.04
12+
steps:
13+
- uses: actions/checkout@v4
14+
- uses: actions/setup-python@v5
15+
with:
16+
python-version: '3.12'
17+
- run: pip install -r requirements.txt -r requirements-dev.txt
18+
- run: pytest -q
19+
920
build-n-publish:
1021
name: Build and publish mdast_cli 🐍 distributions to PyPI
22+
needs: tests
1123
runs-on: ubuntu-22.04
1224
steps:
1325
- uses: actions/checkout@master

.github/workflows/tests.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
pull_request:
6+
7+
jobs:
8+
tests:
9+
name: pytest (unit, contract, smoke, security)
10+
runs-on: ubuntu-22.04
11+
steps:
12+
- uses: actions/checkout@v4
13+
14+
- name: Set up Python 3.12
15+
uses: actions/setup-python@v5
16+
with:
17+
python-version: '3.12'
18+
19+
- name: Install dependencies
20+
run: pip install -r requirements.txt -r requirements-dev.txt
21+
22+
- name: Run full test suite
23+
run: pytest -q
24+
25+
- name: Security tests gate
26+
run: pytest -q -m security

mdast_cli/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = '2026.6.1'

mdast_cli/helpers/const.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,51 @@ class DastState:
4343
# HTTP timeout constants
4444
HTTP_REQUEST_TIMEOUT = 30
4545
HTTP_DOWNLOAD_TIMEOUT = 300
46+
47+
48+
# --- Microservices installation (Clark facade, scanyon-native contract) ---
49+
50+
class ScanStage:
51+
CREATED = 'CREATED'
52+
START = 'START'
53+
WORKING = 'WORKING'
54+
STOP = 'STOP'
55+
SUCCESS = 'SUCCESS'
56+
FAIL = 'FAIL'
57+
58+
59+
class ScanStageStatus:
60+
INITIAL = 'INITIAL'
61+
PROCESSING = 'PROCESSING'
62+
WAITING = 'WAITING'
63+
COMPLETE = 'COMPLETE'
64+
PARTIAL_COMPLETE = 'PARTIAL_COMPLETE'
65+
FAIL = 'FAIL'
66+
67+
68+
# Terminal (stage, status) pairs, confirmed with scanyon FSM (STG-4475).
69+
# STOP is transitional: a stopped scan finishes as SUCCESS/COMPLETE or SUCCESS/PARTIAL_COMPLETE.
70+
TERMINAL_SCAN_PAIRS = {
71+
(ScanStage.SUCCESS, ScanStageStatus.COMPLETE),
72+
(ScanStage.SUCCESS, ScanStageStatus.PARTIAL_COMPLETE),
73+
(ScanStage.FAIL, ScanStageStatus.FAIL),
74+
}
75+
PRE_START_STAGES = {ScanStage.CREATED, ScanStage.START}
76+
ACTIVE_STAGES = {ScanStage.WORKING, ScanStage.STOP}
77+
78+
# Eucalyptus engine contract: platform in `type`, liveness in `status` (not `state`)
79+
ENGINE_ACTIVE_STATUS = 'STARTED'
80+
OS_ANDROID = 'ANDROID'
81+
OS_IOS = 'IOS'
82+
83+
# Installation mode selection (no new CLI flags by contract, env vars only)
84+
MODE_ENV_VAR = 'MDAST_CLI_MODE'
85+
UPLOAD_TIMEOUT_ENV_VAR = 'MDAST_UPLOAD_TIMEOUT'
86+
TLS_VERIFY_ENV_VAR = 'MDAST_TLS_VERIFY'
87+
MODE_AUTO = 'auto'
88+
MODE_MONOLITH = 'monolith'
89+
MODE_MICROSERVICES = 'microservices'
90+
91+
# Clark upload contract (STG-4451): server-side upload_timeout query bounds
92+
UPLOAD_TIMEOUT_MIN = 1
93+
UPLOAD_TIMEOUT_MAX = 300

mdast_cli/helpers/exit_codes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ class ExitCode(IntEnum):
2424

2525
AUTH_ERROR = 7
2626
"""Authentication or authorization error."""
27-
27+
28+
PRECHECK_BLOCKED = 8
29+
"""Scan pre-check returned blocking warnings (microservices installation gate)."""
30+
2831
INTERNAL_ERROR = 1
2932
"""Internal application error (default for unexpected errors)."""
3033

mdast_cli/mdast_scan.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@
2727
from mdast_cli.helpers.exit_codes import ExitCode
2828
from mdast_cli.helpers.helpers import check_app_md5
2929
from mdast_cli_core.token import mDastToken as mDast
30+
from mdast_cli_core.factory import (MODE_MICROSERVICES, ModeDetectionError, resolve_installation_mode,
31+
tls_verify_enabled)
3032
from mdast_cli.cr_report_generator import generate_cr
33+
from mdast_cli import __version__
34+
35+
USER_AGENT = f'mdast_cli/{__version__}'
3136

3237
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s %(message)s',
3338
datefmt='%d/%m/%Y %H:%M:%S', stream=sys.stdout)
@@ -592,6 +597,20 @@ def main():
592597
print(f'DOWNLOAD_PATH={app_file}')
593598
sys.exit(ExitCode.SUCCESS)
594599

600+
try:
601+
installation_mode = resolve_installation_mode(url, token, company_id,
602+
verify=tls_verify_enabled())
603+
except ModeDetectionError as ex:
604+
logger.error(str(ex))
605+
sys.exit(ExitCode.AUTH_ERROR if ex.auth_error else ExitCode.NETWORK_ERROR)
606+
607+
if installation_mode == MODE_MICROSERVICES:
608+
from mdast_cli.ms_flow import run_microservices_flow
609+
run_microservices_flow(arguments, url, token, app_file, appstore_app_md5,
610+
user_agent=USER_AGENT, verify=tls_verify_enabled())
611+
logger.info('Job completed successfully!')
612+
sys.exit(ExitCode.SUCCESS)
613+
595614
mdast = mDast(url, token, company_id)
596615
get_architectures_resp = mdast.get_architectures()
597616

0 commit comments

Comments
 (0)