Read first this article  describing why you should use Softagram dependency checks. 

In this article we walk through the Softagram dependency rules fields, format and actions. In the end there is instruction of how to activate the rule checks and an example .softagram.json dependency rule file.

Dependency Rules JSON reference

Example of dependency rule:

 ["DepRule", "/Project/repo1", "-!->", "/Project/repo2", {"description": "Repo1 should not use repo2!"}]


Every Softagram dependency rule has four mandatory fields:

  1. Dependency Rule type (DepRule, or GrepRule) 
  2. Source element path
  3. DependencyOperator
  4. Target element path

and Optional settings (Settings Map):

Next we walk through all the fields; Dependency Rule Types,  the Source/Target elements,  DependencyOperators and lastly Optional settings that are common for all Rule Types.

DEPENDENCY RULE TYPES

Softagram has two rule types, DepRule is used to detect unwanted dependencies based on the Softagram dependency analysis. GrepRule can be used for more complex cases, as it is not based on detected dependencies but pattern matching.

DepRule

DepRule element can be used to set up simple element path based rules.  Example of DepRule:

  ["DepRule", "/Project/repo1", "-!->", "/Project/repo2", {"description": "This rule ensures repo1 doesn't use repo2"}]

The specification of the rule:

["DepRule", SourcePath, DependencyOperator, TargetPath, SettingsMap]

Source and Target paths are defined in section Element Paths
DependencyOperators are defined in section Dependency Operators
SettingsMap have only general settings defined in section Optional Settings

GrepRule

GrepRule is a bit different from the dependency rules. It can be used to perform grep statements and validate software based on the location of the grep matches.

Currently GrepRule supports only "MustBeLocatedAt" operand

For example, the example GrepRule below checks if the test code is located in correctly named directories:

  [
    "GrepRule",
    [ "#include.*gtest.h"],
    "MustBeLocatedAt",
    ["/test/"],
    {"description": "Always locate test code in test dir."}
  ],

This would mean that writing code that matches to "#include.*gtest.h" pattern will be shown as a dependency issue, unless the file is located under the /test folder.

ELEMENT PATHS

Source and target element paths follow the same syntax. Element path is a slash-delimited path of the element. We talk about element paths instead of file or folder paths, because in Softagram also classes and functions have paths. E.g. /ProjectX/main-repo/src/MyClass.java/MyClass/myFunction

You can use direct element path, multiple paths or aliases in the rule definitions.

Example of a short full path: /ProjectX/main-repo/src/file.h. Full path is always starting with a slash and the project name. Project name is the name of Softagram project! 

Three dots (...) in the beginning of the path looks the partial path everywhere, as in path ".../src/file.h". 

It is also possible to specify some directory as a named variable part using curly braces:   "/Project/repo1/{some-directory}/src"

If both source and target path refer to identically named variable part, the value of that variable part are required to be identical.(Update?)

Multiple source or target paths

To apply the same rule to multiple source or target paths, you can use a list of element paths instead of a single path, example below with "DepRule" and one source and 2 target paths:

 ["DepRule", "/myproj/source/path", "-!->", ["/myproj/target1/path", "/myproj/target2/path"]]

Aliases

To avoid repeating long element paths again and again, you can also define aliases for your most common paths that are used in multiple dependency rules.

["Alias", "MyFile", "/MyProject/my-repo/path/to/my/file"]

After defining an alias like this, you can simply use "MyFile" as the source or target path instead of the full element path.

DEPENDENCY OPERATORS

DependencyOperators between the source and target paths, e.g. "-!->", specifies the matching type. 

These are the operators:

  • -!->     Cannot use operator. Saying A -!-> B means there should not be any dependencies from A to B, and the rule matcher will yield all the violations. When using this operator, B can also be a list of Used expressions instead of a single item.
  • <-!-     Cannot be used by operator. Sometimes it is more convenient to say A should not be used by B. That is possible by A <-!- B.  When using this operator, B can also be a list of Used expressions instead of a single item.
  • <-!->   No relationship between operator. In case there should not be any dependencies between A and B, use two directional operator.
  • -->    Can only use operator. If you know what are the allowed outbound dependencies of some element, this "can only use" operator lets you to specify this so all the other dependencies of A will be matched. For example, A --> B means A can use B but all the other dependencies, e.g. C, are reported as violations. When using this operator, B can also be a list of Used expressions instead of a single item, e.g. A --> [B, C, D],
  • <--   Can only be used by operator. This is a mirror of the above, saying that what are the allowed users of some element. For example, A <-- B means that any inbound dependencies of A will be reported as violations except that are coming from B. As previously, a list of expressions can only be used instead of a single string value.
  • NOTE: in GrepRule, only "MustBeLocatedAt" operand can be used

OPTIONAL SETTINGS

SettingsMap may have following general optional keys: 

  • description 
  • Rule severity
  • ignore_patterns_if_both_equal: Regular expression that will be matched to both user path and used path and if both matches, the dependency will be ignored.
  • ignore_if_used_matches: Regular expression that will be matched to used path, and if matches, the dependency will be ignored.
  • conditions:  There are two ways to use this: In case you use named variable parts in both user and used paths expressions, setting "conditions": [[ "allow_same" ]]  ensures that the match also happens when those variable parts have the identical value.

Description

We recommend always adding a description that tells why the rule exists, as it will be shown up in code-review when someone breaks the rule. You add the description with following format inside the SettingsMap:

{"description": "Your very descriptive note for the violation"}

Rule Severity

DepRule GrepRule may have severity in the settings map. It defines the directives that can adjust the severity of the rule match. These directives are applied so that for any dependency, the last matching directive is effective. Severity directive has four parts: 

  • severity (e.g. warning)
  • user path: e.g. /Project/repo
  • used path: e.g. /Project/repo
  • dependency type: e.g. import

Example: severity directives for an example rule:

"severity": [
    ["warning", "", "", ""], // The first directive...
    ["ignored", "/Project/repo/dir1/file1.ext", "", "deptype1"],
    ["ignored", "/Project/repo/dir2/file1.ext", "", "deptype1"],
    ["ignored", "", "/Some/Component/That/can/be/used/everywhere", ""],
],

In this case the first directive sets all matches of the rule to be warning while some exception cases are set to be ignored. By switching the order of the directives it is possible to turn around how the matching works. In this case opposite order would indicate all matches to be warning, as there is empty string used as the element path. The empty string indicates to match all elements.

Rule severity can be any of critical (default, displayed in red), warning (displayed in yellow), info (displayed in blue), or ignored.

HOW TO ACTIVATE DEPENDENCY RULES

Softagram offers two ways to add new dependency rules for your project:

  1. add the rules into a special .softagram.json  file in your repository's root
  2. add the rules via the Softagram web interface's settings menu:

We recommend using the .softagram.json  approach, as this allows you to keep your dependency rules in version control, making it easier to maintain.

Appendix:

A simple example .softagram.json file:

 [
  [
    "Alias",
    "tests",
    "/MyProject/MyRepo/tests"
  ],
  [
    "Alias",
    "middleware",
    "/MyProject/MyRepo/src/middleware"
  ],
  [
    "Alias",
    "apps",
    "/MyProject/MyRepo/src/apps"
  ],
  [
    "DepRule",
    [
      "middleware",
      "apps"
    ]
    "-!->",
    "tests",
    {
      "description": "Implementation must not depend on any test code"
    }
  ],
  [
    "DepRule",
    "apps",
    "<--",
    "tests",
    {
      "description": "Only test code can depend on application code"
    }
  ],
  [
    "GrepRule",
    [
      "#include.*test.h",
      "#include.*mock.h"
    ],
    "MustBeLocatedAt",
    [
      "/test/"
    ],
    {
      "description": "Test code must reside in the /test folder"
    }
  ],
]
Did this answer your question?