diff --git a/tools/compare_versions/README.md b/tools/compare_versions/README.md new file mode 100644 index 0000000000000000000000000000000000000000..9512b1fbaa1f85fe45a3bcaf32b64a64a03081ee --- /dev/null +++ b/tools/compare_versions/README.md @@ -0,0 +1,118 @@ +# CAPIF Services Scripts + +This repository contains two Bash scripts to **generate CAPIF services code** from OpenAPI definitions and to **compare two versions** of the generated services. + +--- + +## 1. `capif_build.sh` + +This script wraps `openapi-generator` to build Python Flask code from the CAPIF service YAML definitions. +It allows generating all services or just one specific service. + +### Requirements +- `bash` (>= 4.0 recommended) +- [`openapi-generator`](https://openapi-generator.tech/) installed and available in `PATH` + +### Usage +```bash +./capif_build.sh --route <5g_repo_path> --output [--service ] +``` + +### Options +- `--route` : Path to the 5G repo containing the CAPIF YAML files. +- `--output`: Path to the folder where the generated code will be stored. +- `--service` *(optional)*: Name of a single YAML file to build (must match one from the CAPIF services list). + If omitted, **all services** will be built. +- `-h, --help`: Show help message. + +### Examples +Generate all CAPIF services: +```bash +./capif_build.sh --route /path/to/5g_repo --output ./old-version +``` + +Generate only one service: +```bash +./capif_build.sh --route /path/to/5g_repo --output ./new-version \ + --service TS29222_CAPIF_Events_API.yaml +``` + +--- + +## 2. `make_diffs.sh` + +This script compares two folders of generated CAPIF services (e.g., `old-version` vs `new-version`). +It produces: +- A `.diff` file per service (unified diff with `diff -ruN`) +- A `.diffstat` file per service (summary of changes per file, using `diffstat` or `git --no-index --stat`) +- A `summary.txt` file listing the results for all services + +### Requirements +- `sh` or `bash` +- `diff` (standard in Unix-like systems) +- Optional: + - `diffstat` → recommended for per-file summaries + - `git` → used as a fallback for summaries if `diffstat` is not available + +### Usage +```bash +./make_diffs.sh --old --new [--out ] [--service ] +``` + +### Options +- `--old` : Path to the folder with the old version. +- `--new` : Path to the folder with the new version. +- `--out` : (Optional) Output folder for diffs. Default: `./diffs`. +- `--service` : (Optional) Compare only one specific service (folder or file at top level). +- `-h, --help`: Show help message. + +### Examples +Compare all services: +```bash +./make_diffs.sh --old ./old-version --new ./new-version +``` + +Compare only one service: +```bash +./make_diffs.sh --old ./old-version --new ./new-version --service TS29222_CAPIF_Events_API +``` + +Save diffs into a custom folder: +```bash +./make_diffs.sh --old ./old-version --new ./new-version --out ./diffs_run2 +``` + +### Output +After running, the output folder will contain: +``` +diffs/ +├── TS29222_CAPIF_Events_API.diff +├── TS29222_CAPIF_Events_API.diffstat +├── TS29222_CAPIF_Auditing_API.diff +├── TS29222_CAPIF_Auditing_API.diffstat +... +└── summary.txt +``` + +--- + +## Typical Workflow +1. Generate services for the **old version**: + ```bash + ./capif_build.sh --route /path/to/5g_repo --output ./old-version + ``` +2. Generate services for the **new version**: + ```bash + ./capif_build.sh --route /path/to/5g_repo --output ./new-version + ``` +3. Compare them: + ```bash + ./make_diffs.sh --old ./old-version --new ./new-version --out ./diffs + ``` +4. Inspect the `.diff` and `.diffstat` files or open them in a diff viewer like `meld`, `kdiff3`, or `colordiff`. + +--- + +## Tips +- For easier inspection, install GUI diff tools like [Meld](https://meldmerge.org/). +- For API-level changes (OpenAPI/Swagger), you may also use tools like [`oasdiff`](https://github.com/Tufin/oasdiff). diff --git a/tools/compare_versions/download_services.sh b/tools/compare_versions/download_services.sh new file mode 100755 index 0000000000000000000000000000000000000000..440d29e497565929f110f05ebe29ce3fbdd9aafc --- /dev/null +++ b/tools/compare_versions/download_services.sh @@ -0,0 +1,100 @@ +#!/usr/bin/env bash +set -euo pipefail + +# List of CAPIF services +SERVICES=( + "TS29222_CAPIF_API_Invoker_Management_API.yaml" + "TS29222_CAPIF_API_Provider_Management_API.yaml" + "TS29222_CAPIF_Access_Control_Policy_API.yaml" + "TS29222_CAPIF_Auditing_API.yaml" + "TS29222_CAPIF_Discover_Service_API.yaml" + "TS29222_CAPIF_Events_API.yaml" + "TS29222_CAPIF_Logging_API_Invocation_API.yaml" + "TS29222_CAPIF_Publish_Service_API.yaml" + "TS29222_CAPIF_Routing_Info_API.yaml" + "TS29222_CAPIF_Security_API.yaml" +) + +usage() { + cat < --output [--service ] + +Options: + --route Path to the 5G repo containing the YAML files. + --output Folder where the generated code will be stored. + --service (Optional) One of the following YAML files: + ${SERVICES[*]} + -h, --help Show this help message. + +Examples: + $(basename "$0") --route /path/5g --output /tmp/capif + $(basename "$0") --route /path/5g --output /tmp/capif --service TS29222_CAPIF_Events_API.yaml +EOF +} + +# Argument parsing (style: --key value) +ROUTE="" +OUTPUT="" +SERVICE="" + +while [[ $# -gt 0 ]]; do + case "$1" in + --route) ROUTE="${2:-}"; shift 2 ;; + --output) OUTPUT="${2:-}"; shift 2 ;; + --service) SERVICE="${2:-}"; shift 2 ;; + -h|--help) usage; exit 0 ;; + *) echo "Unknown argument: $1" >&2; usage; exit 1 ;; + esac +done + +# Basic validations +[[ -z "$ROUTE" ]] && echo "Error: --route is required." >&2 && usage && exit 1 +[[ -z "$OUTPUT" ]] && echo "Error: --output is required." >&2 && usage && exit 1 + +if ! command -v openapi-generator >/dev/null 2>&1; then + echo "Error: 'openapi-generator' is not installed or not in PATH." >&2 + exit 1 +fi + +if [[ ! -d "$ROUTE" ]]; then + echo "Error: the provided route '$ROUTE' does not exist." >&2 + exit 1 +fi + +mkdir -p "$OUTPUT" + +# If a service is specified, validate it +if [[ -n "$SERVICE" ]]; then + valid=false + for s in "${SERVICES[@]}"; do + if [[ "$s" == "$SERVICE" ]]; then + valid=true + break + fi + done + if [[ "$valid" != true ]]; then + echo "Error: the service '$SERVICE' is not valid." >&2 + echo "It must be one of: ${SERVICES[*]}" >&2 + exit 1 + fi + + path="$ROUTE/$SERVICE" + if [[ ! -f "$path" ]]; then + echo "Error: the service '$SERVICE' does not exist in the route '$ROUTE'." >&2 + exit 1 + fi + + base="${SERVICE%.*}" + echo "Downloading and building service: $SERVICE" + openapi-generator generate -i "$path" -g python-flask -o "$OUTPUT/$base" +else + # Process all services + for svc in "${SERVICES[@]}"; do + path="$ROUTE/$svc" + echo "Downloading and building service: $svc" + openapi-generator generate -i "$path" -g python-flask -o "$OUTPUT/${svc%.*}" + done +fi + +echo "All services have been downloaded and built." diff --git a/tools/compare_versions/make_diffs.sh b/tools/compare_versions/make_diffs.sh new file mode 100755 index 0000000000000000000000000000000000000000..5d34f0b2dc154668a80c66320a3c4d5d1771b4f9 --- /dev/null +++ b/tools/compare_versions/make_diffs.sh @@ -0,0 +1,133 @@ +#!/bin/sh +set -eu + +usage() { + cat < --new [--out ] [--service ] + +Options: + --old Path to the folder with the old version (e.g., old-version) + --new Path to the folder with the new version (e.g., new-version) + --out (Optional) Output folder for diffs. Default: ./diffs + --service (Optional) Single top-level service (file or directory name) to compare + -h, --help Show this help + +Output: + - .diff unified diff (-ruN) for the service + - .diffstat summary (diffstat or git --no-index --stat fallback) + - summary.txt run summary +EOF +} + +OLD="" +NEW="" +OUT="diffs" +ONLY_SERVICE="" + +# Parse args +while [ $# -gt 0 ]; do + case "$1" in + --old) OLD=${2:-}; shift 2 ;; + --new) NEW=${2:-}; shift 2 ;; + --out) OUT=${2:-}; shift 2 ;; + --service) ONLY_SERVICE=${2:-}; shift 2 ;; + -h|--help) usage; exit 0 ;; + *) echo "Unknown argument: $1" >&2; usage; exit 1 ;; + esac +done + +[ -n "$OLD" ] || { echo "Error: --old is required." >&2; usage; exit 1; } +[ -n "$NEW" ] || { echo "Error: --new is required." >&2; usage; exit 1; } + +[ -d "$OLD" ] || { echo "Error: '$OLD' is not a directory." >&2; exit 1; } +[ -d "$NEW" ] || { echo "Error: '$NEW' is not a directory." >&2; exit 1; } + +mkdir -p "$OUT" + +SUMMARY_FILE="$OUT/summary.txt" +: > "$SUMMARY_FILE" # overwrite each run + +have_diffstat=0 +command -v diffstat >/dev/null 2>&1 && have_diffstat=1 + +have_git=0 +command -v git >/dev/null 2>&1 && have_git=1 + +# Build service list +if [ -n "$ONLY_SERVICE" ]; then + # Validate existence in at least one side + if [ ! -e "$OLD/$ONLY_SERVICE" ] && [ ! -e "$NEW/$ONLY_SERVICE" ]; then + echo "Error: service '$ONLY_SERVICE' not found in either '$OLD' or '$NEW'." >&2 + exit 1 + fi + SERVICES_LIST=$ONLY_SERVICE +else + tmp="$(mktemp)" + for p in "$OLD"/* "$NEW"/*; do + [ -e "$p" ] || continue + base=$(basename "$p") + printf '%s\n' "$base" >> "$tmp" + done + SERVICES_LIST=$(sort -u "$tmp") + rm -f "$tmp" + + # If nothing found, exit gracefully + if [ -z "$SERVICES_LIST" ]; then + echo "No services found in '$OLD' or '$NEW'. Nothing to diff." + exit 0 + fi +fi + +echo "Generating diffs into '$OUT'..." +echo "$SERVICES_LIST" | while IFS= read -r svc; do + [ -n "$svc" ] || continue + + left="$OLD/$svc" + right="$NEW/$svc" + out_diff="$OUT/${svc}.diff" + out_stat="$OUT/${svc}.diffstat" + + # Clean previous files to ensure overwrite + rm -f "$out_diff" "$out_stat" + + if [ ! -e "$left" ] && [ ! -e "$right" ]; then + echo "WARN: '$svc' missing in both sides; skipping." | tee -a "$SUMMARY_FILE" + continue + fi + + # 0=no changes, 1=diffs, >1=error + if diff -ruN "$left" "$right" >"$out_diff" 2>&1; then + echo "Service '$svc': no differences." | tee -a "$SUMMARY_FILE" + echo "No differences" >"$out_stat" + else + code=$? + if [ $code -eq 1 ]; then + echo "Service '$svc': differences found -> $(basename "$out_diff")" | tee -a "$SUMMARY_FILE" + if [ $have_diffstat -eq 1 ]; then + if ! diffstat "$out_diff" >"$out_stat" 2>/dev/null; then + echo "Failed to generate diffstat from diff file." >"$out_stat" + fi + elif [ $have_git -eq 1 ] && [ -e "$left" ] && [ -e "$right" ]; then + if ! git -c color.ui=never diff --no-index --stat -- "$left" "$right" >"$out_stat" 2>/dev/null; then + echo "Failed to generate stat with git --no-index." >"$out_stat" + fi + else + { + echo "diffstat and git not available." + echo "Install 'diffstat' for per-file summaries, e.g.:" + echo " - Debian/Ubuntu: sudo apt-get install diffstat" + echo " - macOS (brew): brew install diffstat" + echo " - Fedora/RHEL: sudo dnf install diffstat" + } >"$out_stat" + fi + else + echo "Service '$svc': diff error (exit $code) -> $(basename "$out_diff")" | tee -a "$SUMMARY_FILE" + echo "Diff error (exit $code). See $(basename "$out_diff") for details." >"$out_stat" + fi + fi +done + +echo +echo "Done. Diff files are in: $OUT" +echo "Summary: $SUMMARY_FILE"