From c3665fc29f83cf338e89f51272a8836329c78681 Mon Sep 17 00:00:00 2001
From: Ori Bruk <o.bruk@yadro.com>
Date: Tue, 5 Nov 2024 16:50:49 +0300
Subject: [PATCH] [#68] Ensure compatibility of different API versions with
 each other. Update linters.

Signed-off-by: Ori Bruk <o.bruk@yadro.com>
---
 .forgejo/workflows/pre-commit.yaml |  2 ++
 .pre-commit-config.yaml            | 10 ++++++
 CHANGELOG.md                       | 10 ++++++
 CONTRIBUTING.md                    |  7 +++++
 Makefile                           | 43 +++++++++++++++++++++++++-
 compatibility/accounting.json      |  9 ++++++
 compatibility/apemanager.json      | 19 ++++++++++++
 compatibility/container.json       | 24 +++++++++++++++
 compatibility/netmap.json          | 19 ++++++++++++
 compatibility/object.json          | 49 ++++++++++++++++++++++++++++++
 compatibility/session.json         |  9 ++++++
 doc/release_instructions.md        | 24 ++++++++-------
 version.json                       |  5 +++
 13 files changed, 218 insertions(+), 12 deletions(-)
 create mode 100644 compatibility/accounting.json
 create mode 100644 compatibility/apemanager.json
 create mode 100644 compatibility/container.json
 create mode 100644 compatibility/netmap.json
 create mode 100644 compatibility/object.json
 create mode 100644 compatibility/session.json
 create mode 100644 version.json

diff --git a/.forgejo/workflows/pre-commit.yaml b/.forgejo/workflows/pre-commit.yaml
index 5bf97eb..9bcbce9 100644
--- a/.forgejo/workflows/pre-commit.yaml
+++ b/.forgejo/workflows/pre-commit.yaml
@@ -10,6 +10,8 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - uses: actions/checkout@v3
+        with:
+          fetch-depth: 0
       - name: Install deps
         run: |
           apt update
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 96f3e27..d30e2da 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -22,3 +22,13 @@ repos:
         entry: make fmt
         language: system
         pass_filenames: false
+      - id: make-check-version
+        name: Run make check-version
+        entry: make check-version
+        language: system
+        pass_filenames: false
+      - id: make-compatibility
+        name: Run make compatibility
+        entry: make compatibility
+        language: system
+        pass_filenames: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 70ceb4a..882d4be 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,15 @@
 # Changelog
 
+## [3.0] - 2024-10-08 - Potanin Glacier
+
+### Added
+- Version compatibility information
+- Linter for checking version update
+- Linter for checking json files for version compatibility
+
+### Changed
+- Versioning (semver like 2.14.0 to milestone with patch like 3.0)
+
 ## [Unreleased]
 
 ### Changed
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b6ecdf5..578b067 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -53,6 +53,13 @@ changes. Maybe you will find it convenient to name branch in the
 $ git checkout -b feature/123-something_awesome
 ```
 
+### Maintain version compatibility
+After your code changes, make sure
+
+- Increment the milestone version or patch version in case of bug fixes [Version information](version.json).
+- To add information about compatibility with previous versions of the api for the new code
+  [Compatibility files](compatibility).
+
 ### Test your changes
 After your code changes, make sure
 
diff --git a/Makefile b/Makefile
index c08d609..39e3097 100755
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ SHELL=bash
 
 include help.mk
 
-.PHONY: doc fmt pre-commit unpre-commit pre-commit-run
+.PHONY: doc fmt check-version compatibility pre-commit unpre-commit pre-commit-run
 
 # Regenerate documentation for proto files:
 doc:
@@ -22,6 +22,47 @@ fmt:
 		clang-format -i $$f; \
 	done
 
+# Run version update check in version.json file
+check-version:
+	@protoChanges=$$(git diff --name-only remotes/origin/master | grep '**/*.proto'); \
+	version=$$(git show remotes/origin/master:version.json); \
+	if [ -z "$$version" ] || [ -z "$$protoChanges" ]; then \
+		exit; \
+	fi; \
+	masterMilestone=$$(jq -n --argjson data "$$version" '$$data.milestone'); \
+	masterPatch=$$(jq -n --argjson data "$$version" '$$data.patch'); \
+	milestone=$$(jq -r '.milestone' version.json); \
+	patch=$$(jq -r '.patch' version.json); \
+	if [ "$$masterMilestone" -eq "$$milestone" -a "$$masterPatch" -eq "$$patch" ]; then \
+		echo "⇒ You need to update the api version in the version.json file"; \
+		exit 1; \
+	elif [ "$$masterMilestone" -gt "$$milestone" ]; then \
+		echo "⇒ The milestone cannot be decrement in the version.json file"; \
+		exit 1; \
+	elif [ "$$masterMilestone" -eq "$$milestone" -a "$$masterPatch" -gt "$$patch" ]; then \
+		echo "⇒ The patch cannot be decrement without milestone increment in the version.json file"; \
+		exit 1; \
+	elif [ "$$masterMilestone" -lt "$$milestone" -a "$$patch" -ne 0 ]; then \
+		echo "⇒ The patch should be 0 after milestone increment in the version.json file"; \
+		exit 1; \
+	fi
+
+# Run a compatibility information check \
+Checks that the files that describe compatibility in the compatibility package \
+have the current version information added in the version.json file
+compatibility:
+	@version=$$(jq -r '.milestone' version.json).$$(jq -r '.patch' version.json); \
+	conflicts=0; \
+	for f in `ls **/*.json`; do \
+		for rpc in $$(jq -r '.[] | to_entries | .[].key' $$f); do \
+			if $$(jq -r '.[].'$$rpc' | has("v'$$version'") | not' $$f); then \
+				echo "⇒ No compatibility information found for version" $$version "in file" $$f "for rpc" $$rpc; \
+				conflicts=$$(($conflicts+1)); \
+			fi \
+  		done \
+	done; \
+	exit $$conflicts;
+
 # Activate pre-commit hooks
 pre-commit:
 	pre-commit install --hook-type pre-commit
diff --git a/compatibility/accounting.json b/compatibility/accounting.json
new file mode 100644
index 0000000..393b2ef
--- /dev/null
+++ b/compatibility/accounting.json
@@ -0,0 +1,9 @@
+{
+  "compatibilityInfo": {
+    "Balance": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    }
+  }
+}
diff --git a/compatibility/apemanager.json b/compatibility/apemanager.json
new file mode 100644
index 0000000..832af79
--- /dev/null
+++ b/compatibility/apemanager.json
@@ -0,0 +1,19 @@
+{
+  "compatibilityInfo": {
+    "AddChain": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "RemoveChain": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "ListChains": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    }
+  }
+}
diff --git a/compatibility/container.json b/compatibility/container.json
new file mode 100644
index 0000000..85f3a2f
--- /dev/null
+++ b/compatibility/container.json
@@ -0,0 +1,24 @@
+{
+  "compatibilityInfo": {
+    "Put": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "Delete": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "Get": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "List": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    }
+  }
+}
diff --git a/compatibility/netmap.json b/compatibility/netmap.json
new file mode 100644
index 0000000..5fbe780
--- /dev/null
+++ b/compatibility/netmap.json
@@ -0,0 +1,19 @@
+{
+  "compatibilityInfo": {
+    "LocalNodeInfo": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "NetworkInfo": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "NetmapSnapshot": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    }
+  }
+}
diff --git a/compatibility/object.json b/compatibility/object.json
new file mode 100644
index 0000000..64ef5b4
--- /dev/null
+++ b/compatibility/object.json
@@ -0,0 +1,49 @@
+{
+  "compatibilityInfo": {
+    "Get": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "Put": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "Delete": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "Head": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "Search": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "GetRange": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "GetRangeHash": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "PutSingle": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    },
+    "Patch": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    }
+  }
+}
diff --git a/compatibility/session.json b/compatibility/session.json
new file mode 100644
index 0000000..c9d5eb8
--- /dev/null
+++ b/compatibility/session.json
@@ -0,0 +1,9 @@
+{
+  "compatibilityInfo": {
+    "Create": {
+      "v3.0": {
+        "compatibility": "SUPPORTED"
+      }
+    }
+  }
+}
diff --git a/doc/release_instructions.md b/doc/release_instructions.md
index e7f8af2..c3fee48 100644
--- a/doc/release_instructions.md
+++ b/doc/release_instructions.md
@@ -3,15 +3,17 @@
 This documents outlines the frostfs-api release process and can be used as a TODO
 list for a new release.
 
+## Pre-release actions
+
+Increment the milestone version or patch version in case of bug fixes.
+To add information about compatibility with previous versions of the api for the new code.
+This must be run:
+* `make doc`
+
 ## Pre-release checks
 
 This should run successfully:
-* `make lint`
-
-## Pre-release actions
-
-This must be run:
-* `make doc`
+* `make pre-commit-run`
 
 ## Writing CHANGELOG
 
@@ -30,13 +32,13 @@ Release commit summary should follow the template:
 `Release v<Version> - <Codename island> (<Hangeul>, <Hanja>)`, e.g.:
 
 ```
-Release v2.9.0 - Anmyeondo (안면도, 安眠島)
+Release v3.0 - Anmyeondo (안면도, 安眠島)
 ```
 
 ## Tag the release
 
-Use `vX.Y.Z` tag following the semantic versioning standard. For pre-release
-versions use `vX.Y.Z-rc.N` scheme.
+Use `vX.Y` tag (milestone version with patch). For pre-release
+versions use `vX.Y-rc.N` scheme.
 
 ## Push changes and release tag to Github
 
@@ -45,7 +47,7 @@ that releasing requires admin privileges for the project), both the `master`
 branch update and tag must be pushed simultaneously like this:
 
 ```
-$ git push origin master v2.7.0
+$ git push origin master v2.7
 ```
 
 ## Make a proper Github release
@@ -58,5 +60,5 @@ releases.
 
 ## Post-release actions
 
-* Close corresponding X.Y.Z Github milestone
+* Close corresponding X.Y Github milestone
 * Make announcements in Matrix and Discord channels
diff --git a/version.json b/version.json
new file mode 100644
index 0000000..118bee4
--- /dev/null
+++ b/version.json
@@ -0,0 +1,5 @@
+{
+  "milestone": 3,
+  "patch": 0,
+  "date": "2024-10-08"
+}