Profiles / Programming
VD

Versioning & deployment

Group your scripts, commit, compare, roll back, export, import and package a release.

10 recipes on this page · open in the interactive view

Recipes
  1. First commit of a group of scripts
  2. Change a script, see it as U, commit again and read the history
  3. Hotfix: commit a single script, not the whole group
  4. Review: what changed between two commits
  5. Rollback: restore the previous version of a script
  6. Retire a web service: D in versioning
  7. Export a group to files and import it back
  8. Package a release folder with a manifest
  9. One deployment file: scripts and parameters
  10. Roll a whole group back to a release
executed on MentDB

First commit of a group of scripts

A project is a group of scripts. The release manager creates the group, grants the two web services to it, asks versioning what is new (I = inserted), commits the group with a version message, and checks that nothing is left to commit.

Pipeline
Script
admin
#1. A group with two web services;
group add "tuto_cf_rel";
script create post "tuto.cf.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.cf.price.discount" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 5% discount" {bd* [price] 0.95;} "The discounted price";
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
#2. What does the group contain, what is new;
-> "[members]" (string regexp_replace (group get script "tuto_cf_rel") "\s+" "");
-> "[before]" (version check_group "tuto_cf_rel");
#3. Commit and check again;
-> "[id]" (version commit_group "tuto_cf_rel" "1.0.0: VAT and discount services");
-> "[after]" (version check_group "tuto_cf_rel");
#Cleanup: scripts, group and the commits folder;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
group remove "tuto_cf_rel";
file delete "commits/tuto_cf_rel";
concat "members: " [members] [_n_] "before the commit:" [_n_] [before] "commit id: " [id] [_n_] "after the commit:" [_n_] [after] "(nothing left to commit)";
Result
mentdb
members: {"tuto.cf.price.vat.post":0,"tuto.cf.price.discount.post":0}
before the commit:
STATE : SCRIPT
I : tuto.cf.price.discount.post
I : tuto.cf.price.vat.post
commit id: 20261101172022_Gr3bEL_40P3Wl_CMT
after the commit:
STATE : SCRIPT
(nothing left to commit)
Go further
  • The commit id starts with the date and time: sort commits by name to get the history
  • version commit_group refuses an empty commit with "Sorry, no change detected.": catch it with try in a release script
executed on MentDB

Change a script, see it as U, commit again and read the history

The VAT rate changes. The developer updates the web service, versioning flags it U (updated), the release manager commits version 1.1.0, reads the history of the group, newest first, with the user and the message of each commit, and finds the 1.0 commit with a filter.

Pipeline
Script
admin
#1. A group with two web services;
group add "tuto_cf_rel";
script create post "tuto.cf.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.cf.price.discount" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 5% discount" {bd* [price] 0.95;} "The discounted price";
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
version commit_group "tuto_cf_rel" "1.0.0: first release";
#2. One second later, the VAT rate changes;
wait 1000;
script update "tuto.cf.price.vat.post" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT, overseas rate 8.5%" {bd* [ht] 1.085;} "The price with tax";
-> "[check]" (version check_group "tuto_cf_rel");
version commit_group "tuto_cf_rel" "1.1.0: overseas VAT rate";
#3. History, newest first, the listing starts with a 39 chars editor tag;
-> "[history]" (string substring (version show_commits "tuto_cf_rel" ".*" 10) 39);
-> "[nb]" (string regexp_count [history] "_CMT>");
#The filter is applied before nb_row: the newest 1.0 commit, even with nb_row 1;
-> "[v10]" (string regexp_extract (version show_commits "tuto_cf_rel" "1\.0\..*" 1) "admin - ([^\n]+)" 1);
#Cleanup: scripts, group and the commits folder;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
group remove "tuto_cf_rel";
file delete "commits/tuto_cf_rel";
concat [check] [nb] " commits, newest 1.0 commit found with nb_row 1: " [v10] [_n_] [history];
Result
mentdb
STATE : SCRIPT
U : tuto.cf.price.vat.post
2 commits, newest 1.0 commit found with nb_row 1: 1.0.0: first release
COMMITS
<20261101175213_OBLsck_3JLtfL_CMT>
      admin - 1.1.0: overseas VAT rate
      version show_scriptnames tuto_cf_rel 20261101175213_OBLsck_3JLtfL_CMT;
      version show_scripts tuto_cf_rel 20261101175213_OBLsck_3JLtfL_CMT;
<20261101175212_wqBhoI_aauFao_CMT>
      admin - 1.0.0: first release
      version show_scriptnames tuto_cf_rel 20261101175212_wqBhoI_aauFao_CMT;
      version show_scripts tuto_cf_rel 20261101175212_wqBhoI_aauFao_CMT;
Go further
  • The filter is applied before nb_row: version show_commits "tuto_cf_rel" "1\.0\..*" 1 returns the newest 1.0 commit, even when newer commits exist.
  • The two lines under each commit are ready-to-run commands: copy them into the editor
executed on MentDB

Hotfix: commit a single script, not the whole group

Two scripts changed but only the VAT fix must ship today. version commit_script commits only the scripts whose name starts with a filter; the commit content is listed with show_scriptnames and check_group shows that the discount change is still waiting.

Pipeline
Script
admin
#1. A group with two web services;
group add "tuto_cf_rel";
script create post "tuto.cf.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.cf.price.discount" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 5% discount" {bd* [price] 0.95;} "The discounted price";
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
version commit_group "tuto_cf_rel" "1.0.0: first release";
#2. Two changes are made;
script update "tuto.cf.price.vat.post" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT, overseas rate 8.5%" {bd* [ht] 1.085;} "The price with tax";
script update "tuto.cf.price.discount.post" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 10% discount" {bd* [price] 0.90;} "The discounted price";
#3. Ship only the VAT script;
-> "[id]" (version commit_script "tuto_cf_rel" "tuto.cf.price.vat" "1.0.1 hotfix: VAT only");
-> "[content]" (string substring (version show_scriptnames "tuto_cf_rel" [id]) 39);
-> "[pending]" (version check_group "tuto_cf_rel");
#Cleanup: scripts, group and the commits folder;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
group remove "tuto_cf_rel";
file delete "commits/tuto_cf_rel";
concat "content of the hotfix commit:" [_n_] [content] "still waiting for the next release:" [_n_] [pending];
Result
mentdb
content of the hotfix commit:
<20261101172024_PjNzqB_WoCF2E_CMT>
version show_script tuto_cf_rel 20261101172024_PjNzqB_WoCF2E_CMT tuto.cf.price.vat.post.mql;
still waiting for the next release:
STATE : SCRIPT
U : tuto.cf.price.discount.post
Go further
  • The filter is a prefix: tuto.cf.price would commit both scripts
  • Name your scripts by module (orders.*, invoices.*) so that a prefix is a module
executed on MentDB

Review: what changed between two commits

Before approving a release, the reviewer compares the committed code of the VAT script in version 1.0.0 and 1.1.0. The script reads both versions with show_script, extracts the rate and the description with a regex, and counts the scripts of the whole commit with show_scripts.

Pipeline
Script
admin
#1. A group with two web services;
group add "tuto_cf_rel";
script create post "tuto.cf.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.cf.price.discount" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 5% discount" {bd* [price] 0.95;} "The discounted price";
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
-> "[c1]" (version commit_group "tuto_cf_rel" "1.0.0: first release");
script update "tuto.cf.price.vat.post" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT, overseas rate 8.5%" {bd* [ht] 1.085;} "The price with tax";
-> "[c2]" (version commit_group "tuto_cf_rel" "1.1.0: overseas VAT rate");
#2. The committed code of the script in each version;
-> "[v1]" (version show_script "tuto_cf_rel" [c1] "tuto.cf.price.vat.post.mql");
-> "[v2]" (version show_script "tuto_cf_rel" [c2] "tuto.cf.price.vat.post.mql");
#3. Extract the rate and the description;
-> "[rate1]" (string regexp_extract [v1] "bd\* \S+ ([0-9.]+);" 1);
-> "[rate2]" (string regexp_extract [v2] "bd\* \S+ ([0-9.]+);" 1);
-> "[desc2]" (string regexp_extract [v2] "\"(Price with VAT[^\"]*)\"" 1);
#4. How many scripts in each full commit;
-> "[n1]" (string regexp_count (version show_scripts "tuto_cf_rel" [c1]) "script merge");
-> "[n2]" (string regexp_count (version show_scripts "tuto_cf_rel" [c2]) "script merge");
#Cleanup: scripts, group and the commits folder;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
group remove "tuto_cf_rel";
file delete "commits/tuto_cf_rel";
concat "tuto.cf.price.vat.post: rate " [rate1] " -> " [rate2] [_n_] "new description: " [desc2] [_n_] "scripts in commit 1.0.0: " [n1] ", in commit 1.1.0: " [n2] " (only the changed one)";
Result
mentdb
tuto.cf.price.vat.post: rate 1.2 -> 1.085
new description: Price with VAT, overseas rate 8.5%
scripts in commit 1.0.0: 2, in commit 1.1.0: 1 (only the changed one)
Go further
executed on MentDB

Rollback: restore the previous version of a script

Version 1.1.0 shipped a typo (rate 12 instead of 1.2) and prices are 10 times too high. The release manager finds the 1.0.0 commit by its message, replays its committed code with eval, checks the price is right again, and commits the rollback so the history tells the story.

Pipeline
Script
admin
#1. A group with two web services;
group add "tuto_cf_rel";
script create post "tuto.cf.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.cf.price.discount" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 5% discount" {bd* [price] 0.95;} "The discounted price";
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
version commit_group "tuto_cf_rel" "1.0.0: first release";
script update "tuto.cf.price.vat.post" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 12;} "The price with tax";
version commit_group "tuto_cf_rel" "1.1.0: new VAT";
-> "[bad]" (execute "tuto.cf.price.vat.post" "[ht]" 100);
#2. Find the commit 1.0.0 from its message;
-> "[c1]" (string regexp_extract (version show_commits "tuto_cf_rel" "1\.0\.0.*" 10) "<(\w+_CMT)>" 1);
#3. Replay its code, after the 39 chars editor tag;
eval (string substring (version show_script "tuto_cf_rel" [c1] "tuto.cf.price.vat.post.mql") 39);
-> "[good]" (execute "tuto.cf.price.vat.post" "[ht]" 100);
-> "[state]" (version check_group "tuto_cf_rel");
version commit_group "tuto_cf_rel" "1.1.1: rollback of the VAT script to 1.0.0";
-> "[nb]" (string regexp_count (version show_commits "tuto_cf_rel" ".*" 10) "_CMT>");
#Cleanup: scripts, group and the commits folder;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
group remove "tuto_cf_rel";
file delete "commits/tuto_cf_rel";
concat "price of 100 in 1.1.0: " [bad] [_n_] "after the restore: " [good] [_n_] "versioning sees: " [state] "commits in the history: " [nb];
Result
mentdb
price of 100 in 1.1.0: 1200
after the restore: 120.0
versioning sees: STATE : SCRIPT
U : tuto.cf.price.vat.post
commits in the history: 3
Go further
  • version show_scripts + eval restores every script of a commit in one go
  • Keep the bad commit in the history: the rollback commit documents the incident
executed on MentDB

Retire a web service: D in versioning

The discount service is retired. Ungranting it from the group (or deleting it) makes versioning flag it D (deleted); the release manager commits the removal, then checks with show_scriptnames that the latest commit no longer ships it.

Pipeline
Script
admin
#1. A group with two web services;
group add "tuto_cf_rel";
script create post "tuto.cf.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.cf.price.discount" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 5% discount" {bd* [price] 0.95;} "The discounted price";
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
version commit_group "tuto_cf_rel" "1.0.0: first release";
#2. Retire the discount service from the project;
group ungrant script "tuto.cf.price.discount.post" "tuto_cf_rel";
-> "[granted]" (group is granted script "tuto.cf.price.discount.post" "tuto_cf_rel");
-> "[state]" (version check_group "tuto_cf_rel");
-> "[id]" (version commit_group "tuto_cf_rel" "2.0.0: discount service retired");
-> "[content]" (string substring (version show_scriptnames "tuto_cf_rel" [id]) 39);
#Cleanup: scripts, group and the commits folder;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
group remove "tuto_cf_rel";
file delete "commits/tuto_cf_rel";
concat "still granted: " [granted] [_n_] "versioning sees: " [state] "files written by the 2.0.0 commit: " (string regexp_count [content] "\.mql;");
Result
mentdb
still granted: 0
versioning sees: STATE : SCRIPT
D : tuto.cf.price.discount.post
files written by the 2.0.0 commit: 0
Go further
  • A D commit writes no file: the removal is recorded in the commit and the script disappears from the group snapshot
  • Use group get script before and after to document the scope of a release
executed on MentDB

Export a group to files and import it back

To move a project to another server, script export_group writes one .mql file per script under data/scripts/<group>/ with a scripts.json index. The script exports, lists the files, deletes the scripts as if on an empty server, imports them back and calls the web service again.

Pipeline
Script
admin
#1. A group with two web services;
group add "tuto_cf_rel";
script create post "tuto.cf.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.cf.price.discount" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 5% discount" {bd* [price] 0.95;} "The discounted price";
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
#2. Export the group;
script export_group "tuto_cf_rel";
-> "[files]" (string regexp_replace (file dir_list "data/scripts/tuto_cf_rel") "\s+" "");
-> "[index]" (file load "data/scripts/tuto_cf_rel/scripts.json");
#3. Simulate an empty server;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
-> "[gone]" (script exist "tuto.cf.price.vat.post");
#4. Import and call;
script import_group "tuto_cf_rel";
-> "[back]" (script exist "tuto.cf.price.vat.post");
-> "[price]" (execute "tuto.cf.price.vat.post" "[ht]" 100);
#5. A deleted script comes back without its group: grant it again;
-> "[in_group]" (group is granted script "tuto.cf.price.vat.post" "tuto_cf_rel");
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
file delete "data/scripts/tuto_cf_rel";
#Cleanup: scripts and group;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
group remove "tuto_cf_rel";
concat "exported files: " [files] [_n_] "index: " [index] "exists after delete: " [gone] ", after import: " [back] [_n_] "price of 100: " [price] [_n_] "in the group after import: " [in_group] " (granted again)";
Result
mentdb
exported files: ["tuto.cf.price.vat.post.mql","tuto.cf.price.discount.post.mql","scripts.json"]
index: ["tuto.cf.price.vat.post","tuto.cf.price.discount.post"]
exists after delete: 0, after import: 1
price of 100: 120.0
in the group after import: 0 (granted again)
Go further
  • Each exported file starts with if (script exist ...) {script delete ...}: importing twice is safe
  • Copy the data/scripts/<group> folder to the target server, then run script import_group there
executed on MentDB

Package a release folder with a manifest

The ops team wants a folder per release. The script commits version 1.0.0, exports the group, copies the export into tmp/tuto_cf_release_1.0.0, writes a RELEASE.txt manifest with the commit id and the script list, and checks the content and size of the package.

Pipeline
Script
admin
#1. A group with two web services;
group add "tuto_cf_rel";
script create post "tuto.cf.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.cf.price.discount" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 5% discount" {bd* [price] 0.95;} "The discounted price";
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
-> "[id]" (version commit_group "tuto_cf_rel" "1.0.0: first release");
#2. Export and copy into a release folder;
script export_group "tuto_cf_rel";
-> "[pkg]" "tmp/tuto_cf_release_1.0.0";
file copy_dir "data/scripts/tuto_cf_rel" [pkg];
#3. The manifest;
file create (concat [pkg] "/RELEASE.txt") (concat "release=1.0.0" [_n_] "commit=" [id] [_n_] "scripts=" (file load (concat [pkg] "/scripts.json")) [_n_] "install=script import_group tuto_cf_rel");
-> "[content]" (string regexp_replace (file dir_list [pkg]) "\s+" "");
-> "[lines]" (file count_lines (concat [pkg] "/RELEASE.txt"));
-> "[size]" (> (file size [pkg]) 0);
-> "[manifest]" (file load (concat [pkg] "/RELEASE.txt"));
file delete [pkg];
file delete "data/scripts/tuto_cf_rel";
#Cleanup: scripts, group and the commits folder;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
group remove "tuto_cf_rel";
file delete "commits/tuto_cf_rel";
concat "package: " [content] [_n_] "manifest lines: " [lines] ", package not empty: " [size] [_n_] [manifest];
Result
mentdb
package: ["tuto.cf.price.vat.post.mql","tuto.cf.price.discount.post.mql","RELEASE.txt","scripts.json"]
manifest lines: 5, package not empty: 1
release=1.0.0
commit=20261101172024_LnUoxS_EP5Ndu_CMT
scripts=["tuto.cf.price.vat.post","tuto.cf.price.discount.post"]

install=script import_group tuto_cf_rel
Go further
executed on MentDB

One deployment file: scripts and parameters

For a server without access to the export folder, the release manager builds a single .mql file: the script merge code of every script of the group, generated with script generate merge, plus the merge of the parameter the scripts use. The file is checked and its first lines shown.

Pipeline
Script
admin
#1. A group with two web services;
group add "tuto_cf_rel";
script create post "tuto.cf.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.cf.price.discount" false 1 (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");) "Price after a 5% discount" {bd* [price] 0.95;} "The discounted price";
group grant script "tuto.cf.price.vat.post" "tuto_cf_rel";
group grant script "tuto.cf.price.discount.post" "tuto_cf_rel";
parameter merge "tuto_cf_vat_rate" "1.2";
#2. Merge code of every script of the group;
json load "tuto_cf_members" (group get script "tuto_cf_rel");
-> "[bundle]" (concat "#Bundle of the group tuto_cf_rel;" [_n_]);
json parse_obj "tuto_cf_members" "/" "[name]" "[v]" {
	-> "[bundle]" (concat [bundle] (script generate merge [name]) [_n_]);
};
json unload "tuto_cf_members";
#3. The parameter;
-> "[bundle]" (concat [bundle] (parameter generate merge "tuto_cf_vat_rate"));
file create "tmp/tuto_cf_bundle_1.0.0.mql" [bundle];
-> "[lines]" (file count_lines "tmp/tuto_cf_bundle_1.0.0.mql");
-> "[nb]" (string regexp_count [bundle] "script merge");
file delete "tmp/tuto_cf_bundle_1.0.0.mql";
parameter remove "tuto_cf_vat_rate";
#Cleanup: scripts and group;
script delete "tuto.cf.price.vat.post";
script delete "tuto.cf.price.discount.post";
group remove "tuto_cf_rel";
concat "bundle: " [lines] " lines, " [nb] " scripts" [_n_] [bundle];
Result
mentdb
bundle: 10 lines, 2 scripts
#Bundle of the group tuto_cf_rel;
script merge "tuto.cf.price.vat.post" false 1
  (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");)
  "Price with VAT"
{bd* [ht] 1.2;} "The price with tax";
script merge "tuto.cf.price.discount.post" false 1
  (param (var "[price]" {type is_double [price]} "Price" is_null:false is_empty:false "100");)
  "Price after a 5% discount"
{bd* [price] 0.95;} "The discounted price";
parameter merge "tuto_cf_vat_rate" "1.2";
Go further
  • Merge code is idempotent: run the bundle twice on the target, the result is the same
  • script generate create gives a create request when the target must not already have the script
executed on MentDB

Roll a whole group back to a release

Release 1.1.0 merged a wrong VAT rate and deleted a service by mistake. The release manager evaluates the script merge code stored in the 1.0.0 commit: merge updates the changed script and recreates the deleted one, then a new commit records the rollback.

Pipeline
Script
admin
#1. Release 1.0.0 of a pricing group;
group add "tuto_p4m_rel";
script create post "tuto.p4m.price.vat" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.2;} "The price with tax";
script create post "tuto.p4m.price.round" false 1 (param (var "[p]" {type is_double [p]} "Price" is_null:false is_empty:false "12.345");) "Commercial rounding" {math round [p] 2;} "The rounded price";
group grant script "tuto.p4m.price.vat.post" "tuto_p4m_rel";
group grant script "tuto.p4m.price.round.post" "tuto_p4m_rel";
-> "[r100]" (version commit_group "tuto_p4m_rel" "1.0.0: VAT and rounding");
#2. Release 1.1.0 is a mistake: a wrong rate merged, and the rounding service deleted;
wait 1000;
script merge "tuto.p4m.price.vat.post" false 1 (param (var "[ht]" {type is_double [ht]} "Price without tax" is_null:false is_empty:false "100");) "Price with VAT" {bd* [ht] 1.02;} "The price with tax";
script delete "tuto.p4m.price.round.post";
-> "[bad]" (version check_group "tuto_p4m_rel");
version commit_group "tuto_p4m_rel" "1.1.0: new rate";
-> "[broken]" (execute "tuto.p4m.price.vat.post" "[ht]" 100);
#3. Roll the whole group back: the commit holds the script merge code of every script, after a 39 chars editor tag;
-> "[code]" (string substring (version show_scripts "tuto_p4m_rel" [r100]) 39);
-> "[n]" (string regexp_count [code] "script merge ");
eval [code];
#merge recreated the deleted script, grant it again;
group grant script "tuto.p4m.price.round.post" "tuto_p4m_rel";
-> "[back]" (version check_group "tuto_p4m_rel");
-> "[r111]" (version commit_group "tuto_p4m_rel" "1.1.1: rollback to 1.0.0");
-> "[ok]" (concat (execute "tuto.p4m.price.vat.post" "[ht]" 100) " and " (execute "tuto.p4m.price.round.post" "[p]" 12.345));
#Cleanup: scripts, group and the commits folder;
script delete "tuto.p4m.price.vat.post";
script delete "tuto.p4m.price.round.post";
group remove "tuto_p4m_rel";
file delete "commits/tuto_p4m_rel";
concat "after the bad release:" [_n_] [bad] "100 with VAT = " [broken] [_n_] "rollback: " [n] " script merge statements evaluated" [_n_] [back] "after the rollback: " [ok] [_n_] "rollback commit: " (string regexp_extract [r111] "^[0-9]{8}" 0) "..."
Result
mentdb
after the bad release:
STATE : SCRIPT
D : tuto.p4m.price.round.post
U : tuto.p4m.price.vat.post
100 with VAT = 102.00
rollback: 2 script merge statements evaluated
STATE : SCRIPT
I : tuto.p4m.price.round.post
U : tuto.p4m.price.vat.post
after the rollback: 120.0 and 12.35
rollback commit: 20261101...
Go further
  • script merge creates the script when it is missing and updates it otherwise: the code of a commit can be replayed on any server.
  • A merge does not grant: grant the recreated scripts to the group again, or they stay out of the next commits.

Also in Programming

MB
MQL beginner
Variables, operators, if, loops and your first try/exception: learn MQL step by step.
10 recipes
MD
MQL developer
Atom lists, data types, constants, translations, session variables and error patterns.
10 recipes
BW
Backend & web service developer
Typed scripts, execute / call / include, generated calls and URLs, parameters and logs.
10 recipes
DT
Date & time developer
Calendars, business days, time zones, ISO and epoch, durations, quarters and week numbers.
10 recipes
SE
String & encoding developer
Cleaning, case styles, regex, URL/HTML/Base64 encodings, number bases and encryption.
10 recipes
DV
Data validation developer
IBAN, BIC, SIRET, Luhn, email, IP, UUID, JSON and date checks gathered in a validation report.
10 recipes
JD
JSON & document store developer
In-memory JSON documents, persistent nodes and sequences: build, walk, sort, clean up.
10 recipes
SD
Script developer
Full script lifecycle in the MQL editor: typed parameters, read back, generate, patch, deploy, copy, grant, delete.
10 recipes
EM
Execution modes
execute, call, include, sub_include, parallel, stack: which environment, which checks, which logs.
11 recipes
WS
Web service developer
A small REST resource with get, post, put and delete scripts: validation, JSON answers, codes, grants, URLs and API docs.
10 recipes
AD
Async developer
Asynchronous processing with the stack: push runs now or later, priorities and parallel limits, errors and replays, flows and cleanup.
10 recipes
SD
Scheduler developer
Cron jobs and delayed runs: add, resume, pause, update and remove jobs, prove they ran, and postpone stack runs with a delay condition.
10 recipes
ED
Event developer
Event-driven scripts: file watchers that push each file event to the stack, validation, archiving, errors and safe restarts.
10 recipes
CC
Configuration & connections
Parameters (private, described, locked, deployed), environment variables, sessions and connections used by your scripts.
12 recipes
DE
Debugging & error handling
Coded errors with try/exception, logs with correlation ids, timing, metrics and defensive checks.
10 recipes
SD
SQL developer in MQL
Connections, DDL and DML with sql encode, transactions, cursors, parse, and exports to JSON, CSV, Excel and PDF on a real local database. Run the recipes in order: the first one creates the tuto_db_h2 database.
10 recipes
CD
CRUD & data API developer
Generate SCRUD scripts from a table, patch and install them, write typed read and write APIs, check data quality and manage connections per environment. Uses the tuto_db_h2 database of the SQL developer recipes; the last recipe removes it.
10 recipes
WA
Web application developer
Create a web app, its host name, pages, ajax forms, menu and grants, then remove it all.
10 recipes
AI
Algorithms in MQL
Sorting, searching, recursion, data structures and number algorithms: MQL is a real programming language.
10 recipes
CF
Control flow
if, if force, case, switch, mode, for, while, repeat, break, continue, parallel and synchronized on real cases.
10 recipes
TM
The MQL language in depth
Data types and null, booleans, comments, variables and scope, eval and mql blocks, coded errors and try.
10 recipes
SU
Screen & UI developer (sajax)
Build the screens of MentDB web apps in MQL: Bootstrap grids, forms and every control, ajax buttons, alerts, refreshes and charts.
10 recipes