diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000..8d67bc7a54 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# top-most EditorConfig file +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space + +[*.{java,xml}] +indent_size = 4 +trim_trailing_whitespace = true diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000000..14f5f8fa88 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,15 @@ +version: 2 +updates: + - package-ecosystem: 'maven' + directory: '/' + schedule: + interval: 'daily' + time: '00:00' + open-pull-requests-limit: 15 + + - package-ecosystem: 'github-actions' + directory: '/' + schedule: + interval: 'daily' + time: '00:00' + open-pull-requests-limit: 10 diff --git a/.github/release_drafter.yml b/.github/release_drafter.yml new file mode 100644 index 0000000000..2fe7a3e609 --- /dev/null +++ b/.github/release_drafter.yml @@ -0,0 +1,45 @@ +name-template: 'v$RESOLVED_VERSION' +tag-template: 'v$RESOLVED_VERSION' +categories: + - title: '✨ Features' + labels: + - 'feature' + - title: '🐛 Bug Fixes' + labels: + - 'bug' + - title: '🧰 Maintenance' + labels: + - 'chore' + +change-template: '- $TITLE (#$NUMBER by @$AUTHOR)' +change-title-escapes: '\<*_&' +# You can add # and @ to disable mentions, and add ` to disable code blocks. +version-resolver: + major: + labels: + - 'major' + minor: + labels: + - 'minor' + patch: + labels: + - 'patch' + default: patch +exclude-labels: + - 'skip-changelog' +template: | + ## What's changed + + $CHANGES +autolabeler: + - label: 'chore' + branch: + - '/chore\/.+/' + - label: 'bug' + branch: + - '/bug\/.+/' + - '/bugfix\/.+/' + - '/hotfix\/.+/' + - label: 'feature' + branch: + - '/feature\/.+/' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..22570cfb28 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,30 @@ +--- +name: CI + +on: + push: + branches: + - master + tags-ignore: + - "*" + pull_request: + branches: + - master + workflow_dispatch: {} + +concurrency: + group: build-java-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + uses: jqassistant-tooling/jqassistant-github-actions/.github/workflows/ci.yml@main + with: + publish_snapshots: true + java_test_versions: '[11,17,21]' + secrets: + ossrh_username: ${{ secrets.OSSRH_USERNAME }} + ossrh_password: ${{ secrets.OSSRH_PASSWORD }} + ossrh_signing_key: ${{ secrets.OSSRH_SIGNING_KEY }} + ossrh_signing_password: ${{ secrets.OSSRH_SIGNING_PASSWORD }} + sonar_token: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000000..b998a3ebe8 --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,44 @@ +--- +name: Release + +on: + workflow_dispatch: + inputs: + java_version: + description: Java version to use + type: string + required: false + default: 17 + releaseVersion: + description: "Default version to use when preparing a release." + required: true + default: "X.Y.Z" + developmentVersion: + description: "Default version to use for new local working copy." + required: true + default: "X.Y.Z-SNAPSHOT" + dryRun: + description: "Perform a dry run" + required: true + default: false + type: boolean + assets: + description: "Path expression for Assets to upload." + type: string + required: false + default: "target/checkout/cli/neo4jv?/target/*-distribution.zip" + +jobs: + build: + uses: jqassistant-tooling/jqassistant-github-actions/.github/workflows/release.yml@main + with: + releaseVersion: ${{ github.event.inputs.releaseVersion }} + developmentVersion: ${{ github.event.inputs.developmentVersion }} + dryRun: ${{ github.event.inputs.dryRun }} + assets : ${{ github.event.inputs.assets }} + secrets: + ossrh_username: ${{ secrets.OSSRH_USERNAME }} + ossrh_password: ${{ secrets.OSSRH_PASSWORD }} + ossrh_signing_key: ${{ secrets.OSSRH_SIGNING_KEY }} + ossrh_signing_password: ${{ secrets.OSSRH_SIGNING_PASSWORD }} + sonar_token: ${{ secrets.SONAR_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..b998a3ebe8 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,44 @@ +--- +name: Release + +on: + workflow_dispatch: + inputs: + java_version: + description: Java version to use + type: string + required: false + default: 17 + releaseVersion: + description: "Default version to use when preparing a release." + required: true + default: "X.Y.Z" + developmentVersion: + description: "Default version to use for new local working copy." + required: true + default: "X.Y.Z-SNAPSHOT" + dryRun: + description: "Perform a dry run" + required: true + default: false + type: boolean + assets: + description: "Path expression for Assets to upload." + type: string + required: false + default: "target/checkout/cli/neo4jv?/target/*-distribution.zip" + +jobs: + build: + uses: jqassistant-tooling/jqassistant-github-actions/.github/workflows/release.yml@main + with: + releaseVersion: ${{ github.event.inputs.releaseVersion }} + developmentVersion: ${{ github.event.inputs.developmentVersion }} + dryRun: ${{ github.event.inputs.dryRun }} + assets : ${{ github.event.inputs.assets }} + secrets: + ossrh_username: ${{ secrets.OSSRH_USERNAME }} + ossrh_password: ${{ secrets.OSSRH_PASSWORD }} + ossrh_signing_key: ${{ secrets.OSSRH_SIGNING_KEY }} + ossrh_signing_password: ${{ secrets.OSSRH_SIGNING_PASSWORD }} + sonar_token: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..bec1ca50eb --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +/.idea +*.iml +*.ipr +*~ +*.log +.DS_Store +# Files we would like to have in our local repository, but not in the remote repository +*.ignore +release.properties +pom.xml.releaseBackup +.java-version +*.backup + +# Files generated by the release tooling of jQAssistant +pom.xml.updatetorelease +pom.xml.updatetonextdevversion + +# Ignore temporary shell scripts used to script small tasks +*.sh.ignore + +# Maven related files and directories +target +/.mvn diff --git a/manual/pom.xml b/manual/pom.xml new file mode 100644 index 0000000000..3e4d3306ff --- /dev/null +++ b/manual/pom.xml @@ -0,0 +1,379 @@ + + 4.0.0 + + + com.buschmais.jqassistant + parent + 2.4.0-SNAPSHOT + + + manual + + jQAssistant User Manual + + + The user manual of jQAssistant. + + + + ${project.build.directory}/asciidoc-modules-documentation + ${project.build.directory}/asciidoc-modules-documentation + + + + + + org.asciidoctor + asciidoctor-maven-plugin + + + manual + process-resources + + process-asciidoc + + + + auto + jqassistant.css + ${asciidoc.directory} + + src/main/asciidoc/ + index.adoc + ${project.build.directory}/generated-docs/ + + + ${project.basedir}/src/main/asciidoc + ${project.build.directory}/generated-docs/ + + **/*.png + + + + + + + + xhtml5 + + 3 + 4 + ${asciidoc.directory} + coderay + ${project.groupId} + ${project.artifactId} + ${project.version} + + + + + com.buschmais.jqassistant + jqassistant-maven-plugin + + + + org.apache.maven.plugins + maven-assembly-plugin + + + package-manual + + single + + package + + true + jqassistant-manual-${project.version} + false + + ${project.basedir}/src/main/assembly/manual.xml + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + unpack-manual-source + + unpack-dependencies + + generate-resources + + ${asciidoc.directory} + zip + asciidoc + true + + + + + + + + + + + + com.buschmais.jqassistant.core + parent + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + com.buschmais.jqassistant.core + neo4j + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + + com.buschmais.jqassistant.plugin + yaml2 + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + com.buschmais.jqassistant.plugin + json + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + com.buschmais.jqassistant.plugin + junit + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + com.buschmais.jqassistant.plugin + xml + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + com.buschmais.jqassistant.plugin + common + ${project.version} + + + com.buschmais.jqassistant.plugin + common + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + com.buschmais.jqassistant.plugin + java + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + com.buschmais.jqassistant.plugin + maven3 + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + + + com.buschmais.jqassistant.cli + parent + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + + + com.buschmais.jqassistant + jqassistant-maven-plugin + ${project.version} + zip + asciidoc + provided + + + * + * + + + + + + + + com.buschmais.jqassistant.core + parent + zip + asciidoc + provided + + + com.buschmais.jqassistant.core + neo4j + zip + asciidoc + provided + + + com.buschmais.jqassistant.plugin + common + zip + asciidoc + provided + + + com.buschmais.jqassistant.plugin + java + zip + asciidoc + provided + + + com.buschmais.jqassistant.plugin + json + zip + asciidoc + provided + + + com.buschmais.jqassistant.plugin + junit + zip + asciidoc + provided + + + com.buschmais.jqassistant.plugin + maven3 + zip + asciidoc + provided + + + com.buschmais.jqassistant.plugin + xml + zip + asciidoc + provided + + + com.buschmais.jqassistant.plugin + yaml2 + zip + asciidoc + provided + + + + + com.buschmais.jqassistant.cli + parent + zip + asciidoc + provided + + + + + com.buschmais.jqassistant + jqassistant-maven-plugin + zip + asciidoc + provided + + + + + diff --git a/manual/src/main/asciidoc/images/jqa.jpg b/manual/src/main/asciidoc/images/jqa.jpg new file mode 100644 index 0000000000..987facac85 Binary files /dev/null and b/manual/src/main/asciidoc/images/jqa.jpg differ diff --git a/manual/src/main/asciidoc/include/configuration.adoc b/manual/src/main/asciidoc/include/configuration.adoc new file mode 100644 index 0000000000..0e90e1453d --- /dev/null +++ b/manual/src/main/asciidoc/include/configuration.adoc @@ -0,0 +1,350 @@ +[[core:Configuration]] +== Configuration + +The behavior of jQAssistant accepts a wide range of configuration options, e.g. + +* passing properties to scanner or report plugins +* rules to be executed during analysis including required rule parameters +* define thresholds for warnings and failures during analysis +* location of the database to use, e.g. directory of the embedded Neo4j instance or URL of a remote Neo4j instance + +The configuration options can be passed in different ways: + +* YAML files (recommended) +* system properties or environment variables +* command line options for the Command Line Interface +* configuration options in the Maven build descriptors (pom.xml) + +=== YAML files + +jQAssistant automatically loads YAML files from the following locations of the user home and the working directory: + +* `.jqassistant.yml` or `.jqassistant.yaml` +* `.yml` or `.yaml` files located in the directory or sub-directories of `.jqassistant/` + +NOTE: Configuration files in the user home directory have a lower priority than those in the working directory. + +TIP: The locations can be overridden by command line parameters of the Command Line Utility or the Maven plugin. + +The following options are supported (including default values): + +[source,yaml] +---- +jqassistant: + # Controls whether the execution of jQAssistant shall be skipped + # + # -Djqassistant.skip: true|false + skip: false + + # The list of jQAssistant plugins to load and activate. + # + # Each plugin is identified using its Maven coordinates: + # + # -Djqassistant.plugins[0].group-id + # -Djqassistant.plugins[0].artifact-id + # -Djqassistant.plugins[0].version + # -Djqassistant.plugins[0].classifier (optional) + # -Djqassistant.plugins[0].type (optional) + # -Djqassistant.plugins[0].exclusions[0].group-id (optional) + # -Djqassistant.plugins[0].exclusions[0].artifact-id (optional) + plugins: + - group-id: + artifact-id: + version: + classifier: + type: + exclusions: + - group-id: + artifact-id: + + + # The store configuration + store: + + # URI of the database to connect to. Supported URI schemes are 'file' for embedded databases and 'bolt' for connecting to a running Neo4j instance (3.x+), e.g. + # + # -Djqassistant.store.uri + uri: file://target/jqassistant/store + + # Settings for the embedded Neo4j store + embedded: + + # Enable the HTTP and BOLT connector for the embedded store + # + # -Djqassistant.store.embedded.connector-enabled: true|false + connector-enabled: false + + # The listen address for the HTTP and BOLT connectors + # + # -Djqassistant.store.embedded.listen-address + listen-address: localhost + + # The BOLT connector port + # + # -Djqassistant.store.embedded.bolt-port + bolt-port: 7687 + + # The HTTP connector port + # + # -Djqassistant.store.embedded.http-port + http-port: 7474 + + # The directory from which Neo4j shall load plugins (e.g. APOC) + # + # -Djqassistant.store.embedded.neo4j-plugin-directory + neo4j-plugin-directory: + + # The list of Neo4j plugins to load and activate. + # It uses the "neo4j-plugin-directory" (see above) if provided, otherwise a temporary directory is created. + neo4j-plugins: + # Each plugin is identified using its Maven coordinates: + # + # -Djqassistant.store.embedded.neo4j-plugins[0].group-id + # -Djqassistant.store.embedded.neo4j-plugins[0].artifact-id + # -Djqassistant.store.embedded.neo4j-plugins[0].version + # -Djqassistant.store.embedded.neo4j-plugins[0].classifier (optional) + # -Djqassistant.store.embedded.neo4j-plugins[0].type (optional) + # -Djqassistant.store.embedded.neo4j-plugins[0].exclusions[0].group-id (optional) + # -Djqassistant.store.embedded.neo4j-plugins[0].exclusions[0].artifact-id (optional) + - group-id: + artifact-id: + version: + classifier: + type: + exclusions: + - group-id: + artifact-id: + + + # Settings for connecting to a remote Neo4j store + remote: + + # The user name for authentication. + # + # -Djqassistant.store.username + username: + + # The password for authentication. + # + # -Djqassistant.store.password + password: + + # Activate encryption level for 'bolt' connections. + # + # -Djqassistant.store.encryption: true|false + encryption: true + + #The trust strategy for 'bolt' connections + # + # -Djqassistant.store.trust-strategy: trustAllCertificates|trustCustomCaSignedCertificates|trustSystemCaSignedCertificates + trust-strategy: trustAllCertificates + + # The file containing the custom CA certificate for trust strategy. + # + # -Djqassistant.store.trust-certificate + trust-certificate: + + # Additional properties to be passed to the remote store as key-value pairs. + # + # -Djqassistant.store.properties + properties: + + + # The Scanner configuration + scan: + + # Indicates whether to initially reset the store (i.e. clear all nodes and relationships) before scanning. + # + # -Djqassistant.scan.reset: true|false + reset: true + + # Specifies if a scan shall be continued if an error is encountered. + # + # -Djqassistant.scan.continue-on-error: true|false + continue-on-error: false + + # The items to include for scanning. + include: + + # A list of files to include. + # + #jqassistant.scan.include.files[0] + files: + # - src/folder + + # A list of URLs to include. + # + #jqassistant.scan.include.urls[0] + urls: + # - maven:repository::https://nexus/repository + + # The properties to configure scanner plugins as key-value pairs. The supported properties are plugin specific. + # + # -Djqassistant.scan.properties + properties: + # plugin.property.key: value + + + # The analyze configuration + analyze: + + # The rule configuration + rule: + + # The name of the directory containing project rules. + # + # -Djqassistant.analyze.rule.directory + directory: jqassistant/ + + # The default severity of concepts without an explicit severity. + # + # -Djqassistant.analyze.rule.default-concept-severity: INFO|MINOR|MAJOR|CRITICAL|BLOCKER + default-concept-severity: MINOR + + # The default severity of constraint without an explicit severity. + # + # -Djqassistant.analyze.rule.default-constraint-severity: INFO|MINOR|MAJOR|CRITICAL|BLOCKER + default-constraint-severity: MAJOR + + # The default severity of groups without an explicit severity. + # + # -Djqassistant.analyze.rule.default-group-severity: INFO|MINOR|MAJOR|CRITICAL|BLOCKER + default-group-severity: + + # The baseline configuration + baseline: + + # Enables baseline management for concept and constraint results. + # + # -Djqassistant.analyze.baseline.enabled: true|false + enabled: false + + # The file name for storing the baseline. + # + # -Djqassistant.analyze.baseline.file + file: jqassistant/jqassistant-baseline.xml + + # The concepts to be managed in the baseline (default: none) + # + # -Djqassistant.analyze.baseline-include.concepts[0] + include.concepts: + # - my-concept + + # The constraints to be managed in the baseline (default: all) + # + # -Djqassistant.analyze.baseline.include-constraints[0] + include-constraints: + - "*" + + # The report configuration + report: + + # The properties to configure report plugins. The supported properties are plugin specific. + # + # -Djqassistant.analyze.report.properties + properties: + # plugin.property.key: value + + # Determines the severity level for reporting a warning. + # + # -Djqassistant.analyze.report.warn-on-severity: INFO|MINOR|MAJOR|CRITICAL|BLOCKER|NEVER + warn-on-severity: MINOR + + # Determines the severity level for reporting a failure. + # + # -Djqassistant.analyze.report.fail-on-severity: INFO|MINOR|MAJOR|CRITICAL|BLOCKER|NEVER + fail-on-severity: MAJOR + + # Determines if jQAssistant shall continue the build if failures have been detected. + # + # -Djqassistant.analyze.report.continue-on-failure: true|false + continue-on-failure: false + + # Create an archive containing all generated reports. + # + # -Djqassistant.analyze.report.create-archive: true|false + create-archive: false + + # The concepts to be applied. + # + # -Djqassistant.analyze.concepts[0] + concepts: + # - my-concept + + # The constraints to be validated. + # + # -Djqassistant.analyze.constraints[0] + constraints: + # - my-constraint + + # The constraints to be excluded (e.g. if referenced from a group). + # + # -Djqassistant.analyze.exclude-constraints[0] + exclude-constraints: + # - any-constraint + + + # The groups to be executed. + # + # -Djqassistant.analyze.groups[0] + groups: + # - spring-boot:Default + + # The parameters to be passed to the executed rules. + # + # -Djqassistant.analyze.rule-parameters."parameterName" + rule-parameters: + # parameterName: value + + # Execute concepts even if they have already been applied before + # + # -Djqassistant.analyze.execute-applied-concepts: true|false + execute-applied-concepts: false + + # The execution time [seconds] for rules (concepts/constraints) to show a warning. Can be used as a hint for optimization. + # + # -Djqassistant.analyze.warn-on-rule-execution-time-seconds + warn-on-rule-execution-time-seconds: 5 +---- + +=== Environment Variables + +The names of system properties may be used for environment variables. +Depending on execution environment there may be restrictions on the naming, e.g. not allowing characters like `.`. +Therefore, the following mappings are supported (see https://download.eclipse.org/microprofile/microprofile-config-1.4/microprofile-config-spec.html#default_configsources.env.mapping[Eclipse Microprofile Config]): + +* Exact match (e.g. `jqassistant.scan.continue-on-error`) +* Replace each character that is neither alphanumeric nor _ with _ (e.g. `jqassistant_scan_continue_on_error`) +* Replace each character that is neither alphanumeric nor _ with _; then convert the name to upper case (`JQASSISTANT_SCAN_CONTINUE_ON_ERROR`) + +=== System Properties + +All configuration options can be provided as system properties. +The according names are documented in the YAML example above. + +NOTE: Configuration options defined in a YAML file can be overridden by environment variables of system properties. +The priorities are as following: `System Property` --> `Environment Variable` --> `YAML file`. + +=== Property placeholders + +Values may contain placeholders referering to other properties: + +[source,yaml] +---- +src-dir: ./src + +jqassistant: + scan: + include: + files: + - ${src-dir}/folder1 + - ${src-dir}/folder2 +---- + +The properties in placeholders can be defined in different sources: + +- within the same or other configuration files +- system properties +- environment variables +- Maven or user properties specified in the files pom.xml or settings.xml (for the jQAssistant Maven plugin) + diff --git a/manual/src/main/asciidoc/include/devguide.adoc b/manual/src/main/asciidoc/include/devguide.adoc new file mode 100644 index 0000000000..86f0c2fc5c --- /dev/null +++ b/manual/src/main/asciidoc/include/devguide.adoc @@ -0,0 +1 @@ +== Developer Guide diff --git a/manual/src/main/asciidoc/include/introduction.adoc b/manual/src/main/asciidoc/include/introduction.adoc new file mode 100644 index 0000000000..feb857ddeb --- /dev/null +++ b/manual/src/main/asciidoc/include/introduction.adoc @@ -0,0 +1,467 @@ +== Introduction + +This chapter provides an introduction to the concepts of jQAssistant. + +=== How it works + +The basic idea behind jQAssistant is to integrate the following steps into the build process of a software system: + +1. Scan the generated artifacts and store structural information about them into a database +2. Analyze the structures using _rules_ which are represented by queries +3. Report violations + +jQAssistant itself is a plugin based framework. It comes with a pre-defined set of plugins containing scanners, rules and reports but can be easily extended by +custom rules or implementations. + +As database an embedded instance of Neo4j Community Edition is managed and used by jQAssistant. This means that no setup or configuration of a dedicated server +is required. Neo4j has been chosen because: + +- it is a mature open source graph database +- it allows easy modelling of structural elements of a software and their relations +- it comes with a very expressive and easy to learn query language (Cypher) + +=== Scanner +Scanners are used to import software structures into the database. They are provided by plugins and may support several types of artifacts, e.g. +Java classes, XML files or database structures. +The jQAssistant framework (including the command line or Maven plugin) only provides the infrastructure to run a scan operation on a set +of items, e.g. files, directories or URLs. Every active plugin decides itself if it accepts and imports a given item by checking several +conditions, e.g. file name extensions or a provided scope. The latter is an extra information which provides specific context information +like "java:classpath" for a directory containing Java classes or "maven:repository" for a URL. + +=== Rules +Rules are expressed as Cypher queries and are specified in XML files: + +[source,xml] +.my-rules.xml +---- + + + + + + + + A human readable description of the concept. + + + + + + A human readable description of the concept. + + + + + + + A human readable description of the constraint. + + + + +---- + +Each rule comes with an unique id (e.g. "my-rules:MyConstraint") which can be referenced by other rules. jQAssistant will take care about executing the rules in +the correct order. Furthermore a human readable description shall help developers to understand the rationale behind them. + +TIP: Despite rules are usually referenced by their id it is also possible to use the wildcards `\*` and `?`. +This is especially useful for defining groups and include all constraints that match a specific pattern, e.g. `my-rules:*`. + +==== Groups +A group is a set of rules (i.e. concepts, constraints or other groups) that shall be executed together by including them with the option to overwrite their default severity. +This allows to adjust analysis depth for different types of builds, e.g. a Continuous Integration build (CI) can be configured +to only execute rules with low costs (i.e. execution times) whereas a report build is allowed to run for a longer time with more +expensive checks. + + +==== Concepts +The information created by the scanner represents the structure of a software project on a raw level. _Concept_ rules allow enriching the database with higher +level information to ease the process of writing queries that check for violations (i.e. constraints) . This typically means adding labels, properties or +relations. + +jQAssistant comes with language and framework plugins which include general technical concepts, e.g. + +- "java:MethodOverrides" provided by the Java plugin adds a relation "OVERRIDES" between a method of a sub class to the super class methods it overrides. + +It is recommended to use concepts to enrich the database with information which is specific for the concrete project, e.g. labels can be added to + +- package nodes representing modules of the application ("Module") +- package nodes that represent technical layers ("UI", "EJB") +- class nodes representing elements with a specific role ("Controller", "Model") + +NOTE: Even if the primary intention of a concept is to enrich data it still must provide a return clause. +If a concept returns an empty result a warning will be generated by jQAssistant. +The rationale is that in such case the concept does not match the structure of the application and other rules which depend on it will probably not work as expected. + +TIP: The return clause of the concept shall preferably return a node/relation itself instead of an attribute of it. +With this, XML and HTML reports can provide additional information about the concept. + +==== Constraints +A _Constraint_ is a query which detects violations, e.g. + +- classes with specific roles (e.g. entity, controller, etc.) that are either located in the wrong packages or have names that do not fit defined conventions +- invocations of methods which are deprecated and/or forbidden (e.g. constructors of java.util.Date) +- dependencies to other modules which are not allowed + +A constraint can depend on one or more concepts and usually is referenced by one or more groups. + +NOTE: If a constraint returns a result jQAssistant will report an error including the provided description and information about the returned elements. This +information shall help the developer to understand and fix the problem. + +==== Rule Dependencies + +As shown in the snippets above concepts or constraints may define dependencies to other concepts. There are two approaches: + +`requiresConcept` (XML) or `requiresConcepts` (Asciidoc):: A rule explicitly requires another concept to be executed before. +`providesConcept` (XML) or `providesConcepts` (Asciidoc):: A concept extends another concept by adding the same labels or relationships. + +The rules are referenced by their ids. jQAssistant will ensure that these rules are executed in the correct order. + +Usually dependencies are expressed using `requiresConcept(s)`, e.g. a constraint requires one or more specific concepts. +These concepts again might require other concepts. + +There may be situation where pre-defined constraints and required concepts are defined within a plugin. +In this case it can become necessary to extend such concepts with project-specific concepts, e.g. to work with generated code: + +* Constraints defined by the Spring plugin delivered with jQAssistant verify correct usage of dependency injection +* These constraints exclude all Java types labeled with `:Type:Generated` and therefore require the concept `java:GeneratedType` which is defined by the Java plugin +* This concept may be extended by project specific concepts adding labels `:Type:Generated` and declaring the provided concept `java:GeneratedType` using `providesConcept(s)` + +There might be cases where a concept is provided by a rule plugin which provides the same labels or relationships as an (abstract) concept from another plugin. +In these cases it is possible to perform a dynamic extension (duck-typing) to the abstract by specifying `providesConcept` in a group definition: + +[source,xml] +.my-rules.xml +---- + + + + + + + + + +---- + +==== Severity Of Rules + +A rule may optionally define the _severity_ level. jQAssistant allows to break the build if there are violations in the configured severity level (or higher). +For example, if the severity is set to critical, and if there are violated constraints with blocker and/or critical severity; the build will break. +This feature allows projects to pay down their technical debt in an iterative manner. + +Following severity levels are supported: + +- info +- minor (default for concepts) +- major (default for constraints) +- critical +- blocker + +There is no default severity for groups. If a severity is specified then it is applied to all included elements where no +further severity is given, e.g. + +[source,xml] +.my-rules.xml +---- + + + + + + + + +---- + +Thus execution of the group 'my-rules:MyGroup' will report a violation of constraint... + +- ...'my-rules-Constraint1' with severity 'blocker' (inherited from the group) +- ...'my-rules-Constraint2' with severity 'minor' (specified within the group) + +==== Warnings and Failures + +Based on the severity of violated rules jQAssistant generates warnings and failures. +These are based on configurable thresholds: + +* `jqassistant.analyze.report.warn-on-severity` (default: `minor`) +* `jqassistant.analyze.report.fail-on-severity` (default: `major`) + +The warnings and failures are logged on the console and part of generated reports (e.g. XML or Asciidoc). + +Furthermore, the setting `jqassistant.analyze.report.continue-on-failure` (default: `false`) controls whether jQAssistant will continue or break the build failures have been detected during analysis. + +TIP: If violations are exported to dashboards (e.g. SonarQube, Teamscale) then `jqassistant.analyze.report.continue-on-failure` should be set to `true`. + +==== Script Languages + +Instead of cypher scripting languages like JavaScript, Ruby or Groovy may be used for writing concepts or constraints: + +[source,xml] +.my-scripting-rules.xml +---- + + JavaScript example constraint: returns a result containing the number + of declared methods for each class. + + +---- + +==== Rule Parameters + +Both concepts and constraints may define required parameters: + +[source,xml] +.my-rules.xml +---- + + + + <1> + Labels the root package of the application with "Root". + + SET + root:Root + RETURN + root + ]]> + + + +---- + +<1> Declaration of a required parameter with a default value. +<2> Reference to a parameter in a Cypher query. + +The following parameter types are supported: + +* char +* byte +* short +* int +* long +* float +* double +* boolean +* String + +The values for the required parameters must be provided by the execution context, e.g. the jQAssistant +Maven plugin or the command line utility. A rule may specify a default value which is used if no concrete value +is provided for an execution. + +NOTE: Default values are currently not supported for rules in Asciidoc files. + +For rules expressed in Cypher the parameters are referenced by `{...}` placeholders. For scripts the values are passed +as parameters, i.e. they may be used directly in the code. + +==== Result verification + +The default strategy (`rowCount`) verifies a result of a concept or constraint by counting the number of returned rows, i.e. + +- a concept is valid if it returns at least one row +- a constraint is valid if it returns no row + +This behavior can be customized by specifing `min` and `max` thresholds: + +[source,xml] +---- + + A human readable description of the constraint. + + + + + +---- + +It is also possible to verify aggregated results reported as numeric values in a column, e.g. + +[source,xml] +---- + + A human readable description of the constraint. + + + + + +---- + +* For each returned row the value of the column "Count" will be verified following the same principles as described above +* The rule fails if at least one returned row does not match the expected result +* The attribute `column`/`aggregationColumn` can be omitted, in this case the first column of the result is evaluated +* Similar to the row count verification the attributes `min`/`aggregationMin` and `max`/`aggregationMax` can be specified for individual thresholds + +==== Report + +A rule may select a specific report plugin and pass properties to it: + +[source,xml] +---- + + A human readable description of the concept. + (n) + ... + RETURN + m, n + ]]> + + value + + +---- + +===== Primary Column + +If a rule reports more than one column it might be necessary to specify the column which contains the primary element +the rule refers to, e.g. the Java class. The information may be evaluated by reporting tools, e.g. for creating issues +in SonarQube: + +[source,xml] +---- + + A human readable description of the concept. + (n) + ... + RETURN + m, n + ]]> + + +---- + +NOTE: The first column will be used automatically if no primary column is explicitly specified. + +==== Baseline Management + +Introducing rules to an existing codebase usually leads to a large number of existing violations. +It is a common strategy to suppress them and only check for new ones. +This can be achieved by enabling baseline management in the configuration: + +[source,yaml] +.jqassistant.yml +---- +jqassistant: + analyze: + baseline: + enabled: true +---- + +Existing violations will be reported during the first analysis. +At the same time a file `jqassistant-baseline.xml` will be created in the rule directory containing these violations as entries. These will no longer be reported by subsequent executions. + +The file `jqassistant-baseline.xml` is supposed to be checked in into the VCS. +If an entry contained in the baseline file is no longer detected during an analysis, then the according entry will be removed from the file which can be updated in the VCS again. + +TIP: Entries in the baseline can be removed manually if these entries no longer shall be suppressed during analysis. To achieve this the according ` ... ` must be deleted and the baseline file updated in the VCS. + +If baseline management is enabled the by default all constraint violations will be included. +This can be fine-tuned by adding further configuration properties: + +[source,yaml] +.jqassistant.yml +---- +jqassistant: + analyze: + baseline: + enabled: true + include-constraints: + - "spring-injection:*" + include-concepts: + - "spring-injection:*" +---- + +TIP: By default concepts are not included in the baseline but this can be activated (see above). +This is useful for monitoring existing concepts. +If they disappear for any reason (i.e. changed code or updated rules), then the baseline file will be updated and the change be reported by the VCS. + diff --git a/manual/src/main/asciidoc/include/license.adoc b/manual/src/main/asciidoc/include/license.adoc new file mode 100644 index 0000000000..16d056be56 --- /dev/null +++ b/manual/src/main/asciidoc/include/license.adoc @@ -0,0 +1,2 @@ +== License +jQAssistant is contributed under http://www.gnu.org/licenses/gpl-3.0.html[GNU General Public License, v3]. diff --git a/manual/src/main/asciidoc/include/overview.adoc b/manual/src/main/asciidoc/include/overview.adoc new file mode 100644 index 0000000000..30a77e8122 --- /dev/null +++ b/manual/src/main/asciidoc/include/overview.adoc @@ -0,0 +1,25 @@ +== Overview + +jQAssistant is a QA tool which allows the definition and validation of project specific rules on a structural level. It is built upon the +graph database http://neo4j.org[Neo4j] and can easily be plugged into the build process to automate detection of constraint violations +and generate reports about user defined concepts and metrics. + +Example use cases: + +- Enforce naming conventions, e.g. EJBs, JPA entities, test classes, packages, maven modules etc. +- Validate dependencies between modules of your project +- Separate API and implementation packages +- Detect common problems like cyclic dependencies or tests without assertions + +The rules are expressed in Cypher - the easy-to-learn query language of Neo4j: + +[source] +---- +MATCH + (t:Test:Method) +WHERE NOT + (t)-[:INVOKES]->(:Assert:Method) +RETURN + t AS TestWithoutAssertion +---- + diff --git a/manual/src/main/asciidoc/include/plugins.adoc b/manual/src/main/asciidoc/include/plugins.adoc new file mode 100644 index 0000000000..2ad40ea522 --- /dev/null +++ b/manual/src/main/asciidoc/include/plugins.adoc @@ -0,0 +1,17 @@ +== Plugins + +This section provides detailed descriptions of all distributed plugins. + +:leveloffset: 2 + +// +// Please insert new documents in alphabetical order!!!! +// + +include::{docRoot}/com.buschmais.jqassistant.plugin/java/asciidoc/plugin.adoc[] +include::{docRoot}/com.buschmais.jqassistant.plugin/json/asciidoc/plugin.adoc[] +include::{docRoot}/com.buschmais.jqassistant.plugin/junit/asciidoc/plugin.adoc[] +include::{docRoot}/com.buschmais.jqassistant.plugin/maven3/asciidoc/plugin.adoc[] +include::{docRoot}/com.buschmais.jqassistant.plugin/xml/asciidoc/plugin.adoc[] + +:leveloffset: 0 diff --git a/manual/src/main/asciidoc/include/quickstart.adoc b/manual/src/main/asciidoc/include/quickstart.adoc new file mode 100644 index 0000000000..0d43fbe150 --- /dev/null +++ b/manual/src/main/asciidoc/include/quickstart.adoc @@ -0,0 +1,169 @@ +== Quickstart + +=== Command Line + +==== Requirements + +- Java Development Kit 11 or later +- optional _JAVA_HOME_ environment variable pointing to the JDK to be used for jQAssistant + +==== Installation + +* The latest command line distributions are available on Maven Central. Use one of the links below for selecting the bundled Neo4j version depending on your local JDK, select `Browse` and download the `*-distribution.zip` file: +** https://central.sonatype.com/artifact/com.buschmais.jqassistant.cli/jqassistant-commandline-neo4jv5/versions[jQAssistant for JDK 17 or later] (includes Neo4j v5.x, _recommended_) +** https://central.sonatype.com/artifact/com.buschmais.jqassistant.cli/jqassistant-commandline-neo4jv4/versions[jQAssistant for JDK 11] (includes Neo4j v4.x) +* Unpack the distribution, a directory + `jqassistant-commandline-neo4jv____-__` will be created + +NOTE: jQAssistant releases until 2.0.x are available https://central.sonatype.com/artifact/com.buschmais.jqassistant.cli/jqassistant-commandline-distribution/versions[here]. + +==== Scan + +[source] +.Windows +---- +bin\jqassistant.cmd scan -f lib +---- + +[source] +.Linux +---- +bin/jqassistant.sh scan -f lib +---- + +* The JAR files contained in the lib/ folder will be scanned. + +==== Explore + +[source] +.Windows +---- +bin\jqassistant.cmd server +---- + +[source] +.Linux +---- +bin/jqassistant.sh server +---- + +* Open a browser and navigate to http://localhost:7474[http://localhost:7474] +* Enter the following query in the top level area and hit Ctrl-Enter: + +[source] +---- +MATCH + (a:Artifact)-[:CONTAINS]->(t:Type)-[:DECLARES]->(m:Method) +RETURN + a.fileName as Artifact, t.fqn AS Type, count(t) AS DeclaredMethods +ORDER BY + DeclaredMethods DESC +LIMIT 20 +---- + +=== Maven + +==== Requirements + +- Maven 3.5 or later +- Java Development Kit 11 or later + +==== Add the plugin + +Add the following lines to the parent pom.xml file of your project: + +[source,xml,subs=attributes+] +---- + + + + {projectVersion} + + + + + com.buschmais.jqassistant + jqassistant-maven-plugin + ${jqassistant.version} + + + + scan + analyze + + + + + + + +---- + +==== Add a rule + +Within your parent module create a directory `jqassistant` and a file `my-rules.adoc`: + +[source,xml] +.jqassistant/my-rules.adoc +.... += My Project + +// Include a summary of all executed rules and their status +include::jQA:Summary[] + +[[default]] +[role=group,includesConstraints="my-rules:*"] +== Default Rules + +[[my-rules:TestClassName]] +[source,cypher,role=constraint,requiresConcepts="junit4:TestClass"] +---- +MATCH + (t:Junit4:Test:Class) +WHERE NOT + t.name ends with "Test" +RETURN + t AS InvalidTestClass +---- + +== Imported Rules + +// Include specific rules that have been executed and their results. +include::jQA:Rules[concepts="junit*:*"] +.... + +==== Run the build + +Execute the following command from your parent module: + +[source] +---- +mvn install +---- + +The build will fail with the rule's message if it is violated. + +The HTML report generated from the Asciidoc file including all results is available from `target/jqassistant/report/asciidoc/index.html`. + +==== Explore your application + +jQAssistant comes with an integrated Neo4j server, you can run it using + +[source] +---- +mvn jqassistant:server +---- + +* Open a browser and navigate to http://localhost:7474[http://localhost:7474] +* Enter the follwoing query in the top level area and hit Ctrl-Enter: + +[source] +---- +MATCH + (t:Type)-[:DECLARES]->(m:Method) +RETURN + t.fqn AS Type, count(t) AS DeclaredMethods +ORDER BY + DeclaredMethods DESC +LIMIT 20 +---- diff --git a/manual/src/main/asciidoc/index.adoc b/manual/src/main/asciidoc/index.adoc new file mode 100644 index 0000000000..d09385cba8 --- /dev/null +++ b/manual/src/main/asciidoc/index.adoc @@ -0,0 +1,29 @@ +:numbered: +:sectnumlevels: 2 +:toclevels: 2 + += jQAssistant User Manual +jQAssistant @jqassistant + +image::images/jqa.jpg[] + +This document describes the concepts of jQAssistant and usage information. + +[abstract] +include::include/overview.adoc[] + +include::include/license.adoc[] + +include::include/quickstart.adoc[] + +include::include/introduction.adoc[] + +include::{docRoot}/com.buschmais.jqassistant.cli/parent/asciidoc/readme.adoc[] + +include::{docRoot}/com.buschmais.jqassistant/jqassistant-maven-plugin/asciidoc/readme.adoc[] + +include::include/configuration.adoc[] + +include::include/plugins.adoc[] + + diff --git a/manual/src/main/asciidoc/jqassistant.css b/manual/src/main/asciidoc/jqassistant.css new file mode 100644 index 0000000000..3d13152c17 --- /dev/null +++ b/manual/src/main/asciidoc/jqassistant.css @@ -0,0 +1,1784 @@ + +/**** + **** + ****/ + +/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */ +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { + display: block; +} + +audio, canvas, video { + display: inline-block; +} + +audio:not([controls]) { + display: none; + height: 0; +} + +[hidden] { + display: none; +} + +html { + background: #fff; + color: #3d3a37; + font-family: 'Open Sans', sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; +} + +body { + font-family: 'Open Sans', sans-serif; + color: #3d3a37; + margin: 0; +} + +a:focus { + outline: thin dotted; +} + +a:active, a:hover { + outline: 0; +} + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +abbr[title] { + border-bottom: 1px dotted; +} + +b, strong { + font-weight: bold; +} + +dfn { + font-style: italic; +} + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0; +} + +mark { + background: #ff0; + color: #3d3a37; +} + +code, kbd, pre, samp { + font-family: monospace, serif; + font-size: 1em; + background-color: #f2f2f2; +} + +pre { + white-space: pre-wrap; +} + +q { + quotes: "\201C" "\201D" "\2018" "\2019"; +} + +small { + font-size: 80%; +} + +sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +img { + border: 0; +} + +svg:not(:root) { + overflow: hidden; +} + +figure { + margin: 0; +} + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +legend { + border: 0; + padding: 0; +} + +button, input, select, textarea { + font-family: inherit; + font-size: 100%; + margin: 0; +} + +button, input { + line-height: normal; +} + +button, select { + text-transform: none; +} + +button, html input[type="button"], input[type="reset"], input[type="submit"] { + -webkit-appearance: button; + cursor: pointer; +} + +button[disabled], html input[disabled] { + cursor: default; +} + +input[type="checkbox"], input[type="radio"] { + box-sizing: border-box; + padding: 0; +} + +input[type="search"] { + -webkit-appearance: textfield; + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + box-sizing: content-box; +} + +input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +button::-moz-focus-inner, input::-moz-focus-inner { + border: 0; + padding: 0; +} + +textarea { + overflow: auto; + vertical-align: top; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +*, *:before, *:after { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +html, body { + font-size: 100%; +} + +body { + background: white; + color: #1b1a19; + padding: 0; + margin: 0; + font-family: 'Open Sans', sans-serif; + font-weight: normal; + font-style: normal; + line-height: 1; + position: relative; + cursor: auto; +} + +a:hover { + cursor: pointer; +} + +a:focus { + outline: none; +} + +img, object, embed { + max-width: 100%; + height: auto; +} + +object, embed { + height: 100%; +} + +img { + -ms-interpolation-mode: bicubic; +} + +#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { + max-width: none !important; +} + +.left { + float: left !important; +} + +.right { + float: right !important; +} + +.text-left { + text-align: left !important; +} + +.text-right { + text-align: right !important; +} + +.text-center { + text-align: center !important; +} + +.text-justify { + text-align: justify !important; +} + +.hide { + display: none; +} + +.antialiased, body { + -webkit-font-smoothing: antialiased; +} + +img { + display: inline-block; + vertical-align: middle; +} + +textarea { + height: auto; + min-height: 50px; +} + +select { + width: 100%; +} + +p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { + font-size: 1.21875em; + line-height: 1.6; +} + +.subheader, #content #toctitle, .admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title, .tableblock > caption { + line-height: 1.4; + color: #7a2518; + font-weight: 300; + margin-top: 0.2em; + margin-bottom: 0.5em; +} + +div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { + margin: 0; + padding: 0; + direction: ltr; +} + +a { + color: #74bc00; + text-decoration: underline; + line-height: inherit; +} + +a:hover, a:focus { + color: #1b1a19; +} + +a img { + border: none; +} + +p { + font-family: inherit; + font-weight: normal; + font-size: 1em; + line-height: 1.6; + margin-bottom: 1.25em; + text-rendering: optimizeLegibility; +} + +p aside { + font-size: 0.875em; + line-height: 1.35; + font-style: italic; +} + +h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { + font-family: 'Open Sans', serif; + font-weight: normal; + font-style: normal; + color: #578d00; + text-rendering: optimizeLegibility; + margin-top: 1em; + margin-bottom: 0.5em; + line-height: 1.2125em; +} + +h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { + font-size: 60%; + color: #e99b8f; + line-height: 0; +} + +h1 { + color: #1b1a19; + font-size: 2.125em; +} + +h2 { + font-size: 1.6875em; +} + +h3, #toctitle, .sidebarblock > .content > .title { + font-size: 1.375em; +} + +h4 { + font-size: 1.125em; +} + +h5 { + font-size: 1.125em; +} + +h6 { + font-size: 1em; +} + +hr { + border: solid #e3e3e2; + border-width: 1px 0 0; + clear: both; + margin: 1.25em 0 1.1875em; + height: 0; +} + +em, i { + font-style: italic; + line-height: inherit; +} + +strong, b { + font-weight: bold; + line-height: inherit; +} + +small { + font-size: 60%; + line-height: inherit; +} + +code { + font-family: Consolas, "Liberation Mono", Courier, monospace; + font-weight: normal; + color: #6d180b; +} + +ul, ol, dl { + font-size: 1em; + line-height: 1.6; + margin-bottom: 1.25em; + list-style-position: outside; + font-family: inherit; +} + +ul, ol { + margin-left: 1.5em; +} + +ul li ul, ul li ol { + margin-left: 1.25em; + margin-bottom: 0; + font-size: 1em; +} + +ul.square li ul, ul.circle li ul, ul.disc li ul { + list-style: inherit; +} + +ul.square { + list-style-type: square; +} + +ul.circle { + list-style-type: circle; +} + +ul.disc { + list-style-type: disc; +} + +ul.no-bullet { + list-style: none; +} + +ol li ul, ol li ol { + margin-left: 1.25em; + margin-bottom: 0; +} + +dl dt { + margin-bottom: 0.3125em; + font-weight: bold; +} + +dl dd { + margin-bottom: 1.25em; +} + +abbr, acronym { + text-transform: uppercase; + font-size: 90%; + color: #e3e3e2; + border-bottom: 1px dotted #dddddd; + cursor: help; +} + +abbr { + text-transform: none; +} + +blockquote { + margin: 0 0 1.25em; + padding: 0.5625em 1.25em 0 1.1875em; + border-left: 1px solid #e3e3e2; +} + +blockquote cite { + display: block; + font-size: inherit; + color: #3d3a37; +} + +blockquote cite:before { + content: "\2014 \0020"; +} + +blockquote cite a, blockquote cite a:visited { + color: #3d3a37; +} + +blockquote, blockquote p { + line-height: 1.6; + color: #6f6f6f; +} + +.vcard { + display: inline-block; + margin: 0 0 1.25em 0; + border: 1px solid #dddddd; + padding: 0.625em 0.75em; +} + +.vcard li { + margin: 0; + display: block; +} + +.vcard .fn { + font-weight: bold; + font-size: 0.9375em; +} + +.vevent .summary { + font-weight: bold; +} + +.vevent abbr { + cursor: auto; + text-decoration: none; + font-weight: bold; + border: none; + padding: 0 0.0625em; +} + +@media only screen and (min-width: 768px) { + h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { + line-height: 1.4; + } + + h1 { + font-size: 2.75em; + } + + h2 { + font-size: 2.3125em; + } + + h3, #toctitle, .sidebarblock > .content > .title { + font-size: 1.6875em; + } + + h4 { + font-size: 1.4375em; + } +} + +.print-only { + display: none !important; +} + +@media print { + * { + background: transparent !important; + color: #000 !important; + box-shadow: none !important; + text-shadow: none !important; + } + + a, a:visited { + text-decoration: underline; + } + + a[href]:after { + content: " (" attr(href) ")"; + } + + abbr[title]:after { + content: " (" attr(title) ")"; + } + + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { + content: ""; + } + + pre, blockquote { + border: 1px solid #999; + page-break-inside: avoid; + } + + thead { + display: table-header-group; + } + + tr, img { + page-break-inside: avoid; + } + + img { + max-width: 100% !important; + } + + @page { + margin: 0.5cm; + } + + p, h2, h3, #toctitle, .sidebarblock > .content > .title { + orphans: 3; + widows: 3; + } + + h2, h3, #toctitle, .sidebarblock > .content > .title { + page-break-after: avoid; + } + + .hide-on-print { + display: none !important; + } + + .print-only { + display: block !important; + } + + .hide-for-print { + display: none !important; + } + + .show-for-print { + display: inherit !important; + } +} + +table { + background: white; + margin-bottom: 1.25em; + border: solid 1px #e3e3e2; +} + +table thead, table tfoot { + background: whitesmoke; + font-weight: bold; +} + +table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { + padding: 0.5em 0.625em 0.625em; + font-size: inherit; + color: #1b1a19; + text-align: left; +} + +table tr th, table tr td { + padding: 0.5625em 0.625em; + font-size: inherit; + color: #1b1a19; +} + +table tr.even, table tr.alt, table tr:nth-of-type(even) { + background: #f9f9f9; +} + +table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { + display: table-cell; + line-height: 1.6; +} + +.clearfix:before, .clearfix:after, .float-group:before, .float-group:after { + content: " "; + display: table; +} + +.clearfix:after, .float-group:after { + clear: both; +} + +*:not(pre) > code { + font-size: 0.9375em; + padding: 1px 3px 0; + white-space: nowrap; + background-color: #f2f2f2; + border: 1px solid #cccccc; + -webkit-border-radius: 4px; + border-radius: 4px; + text-shadow: none; +} + +pre, pre > code { + line-height: 1.4; + color: inherit; + font-family: Consolas, "Liberation Mono", Courier, monospace; + font-weight: normal; +} + +kbd.keyseq { + color: #3d3a37; +} + +kbd:not(.keyseq) { + display: inline-block; + color: #222222; + font-size: 0.75em; + line-height: 1.4; + background-color: #F7F7F7; + border: 1px solid #ccc; + -webkit-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.2), 0 0 0 2px white inset; + margin: -0.15em 0.15em 0 0.15em; + padding: 0.2em 0.6em 0.2em 0.5em; + vertical-align: middle; + white-space: nowrap; +} + +kbd kbd:first-child { + margin-left: 0; +} + +kbd kbd:last-child { + margin-right: 0; +} + +.menuseq, .menu { + color: #1b1a19; +} + +p a > code:hover { + color: #74bc00; +} + +#header, #content, #footnotes, #footer { + width: 100%; + margin-left: auto; + margin-right: auto; + margin-top: 0; + margin-bottom: 0; + max-width: 62.5em; + *zoom: 1; + position: relative; + padding-left: 0.9375em; + padding-right: 0.9375em; +} + +#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { + content: " "; + display: table; +} + +#header:after, #content:after, #footnotes:after, #footer:after { + clear: both; +} + +#header { + margin-bottom: 2.5em; +} + +#header > h1 { + color: #1b1a19; + font-weight: normal; + border-bottom: 2px solid #1b1a19; + margin-bottom: -28px; + padding-bottom: 32px; +} + +#header span { + color: #747270; +} + +#header #revnumber { + text-transform: capitalize; +} + +#header br { + display: none; +} + +#header br + span { + padding-left: 3px; +} + +#header br + span:before { + content: "\2013 \0020"; +} + +#header br + span.author { + padding-left: 0; +} + +#header br + span.author:before { + content: ", "; +} + +#toc { + border-bottom: 2px solid #74bc00; + padding-bottom: 1.25em; + background-color: #e3e3e2; +} + +#toc > ul { + margin-left: 0.25em; +} + +#toc ul.sectlevel0 > li > a { + font-style: italic; +} + +#toc ul.sectlevel0 ul.sectlevel1 { + margin-left: 0; + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +#toc ul { + list-style-type: none; +} + +#toctitle { + color: #578d00; +} + +@media only screen and (min-width: 1280px) { + + body.toc { + padding-left: 15em; + padding-right: 0; + } + + #toc.toc { + margin-top: 0!important; + position: fixed; + width: 15em; + left: 0; + top: 0; + border-right: 1px solid #efefed; + border-top-width: 0!important; + border-bottom-width: 0!important; + z-index: 1000; + padding: 1.25em 1em; + height: 100%; + overflow: auto; + } + + #toc.toc #toctitle { + margin-top: 0; + } + + #toc.toc > ul { + font-size: .95em; + margin-bottom: 0; + } + + #toc.toc ul ul { + margin-left: 0; + padding-left: 1.25em; + } + + #toc.toc ul.sectlevel0 ul.sectlevel1 { + padding-left: 0; + margin-top: 0.5em; + margin-bottom: 0.5em; + } + + body.toc.toc-right { + padding-left: 0; + padding-right: 20em; + } + + body.toc.toc-right #toc.toc { + border-right: 0; + border-left: 1px solid #ebebeb; + left: auto; + right: 0; + } + + #content, #footnotes, #footer { + width: 100%; + max-width: 62.5em; + margin-left: 16.25em; + margin-right: auto; + margin-top: 0; + margin-bottom: 0em; + *zoom: 1; + position: relative; + padding-left: 0.9375em; + padding-right: 0.9375em; + } + + #header { + width: 100%; + max-width: 62.5em; + margin-left: 16.25em; + margin-right: auto; + margin-top: 0; + margin-bottom: 4em; + *zoom: 1; + position: relative; + padding-left: 0.9375em; + padding-right: 0.9375em; + } +} + +#content #toc { + border-style: solid; + border-width: 1px; + border-color: #e3e3e2; + margin-bottom: 1.25em; + padding: 1.25em; + background: #f3f3f2; + border-width: 0; + -webkit-border-radius: 4px; + border-radius: 4px; +} + +#content #toc > :first-child { + margin-top: 0; +} + +#content #toc > :last-child { + margin-bottom: 0; +} + +#content #toc a { + text-decoration: none; +} + +#content #toctitle { + font-weight: bold; + font-family: 'Open Sans', sans-serif; + font-size: 1em; + padding-left: 0.125em; +} + +#footer { + width: 100%; + max-width: 62.5em; + padding: 1.25em; +} + +#footer-text { + color: #c8c8c8; + line-height: 1.44; +} + +.sect1 { + padding-bottom: 1.25em; +} + +.sect1 + .sect1 { + border-top: 2px solid #74bc00; +} + +#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { + position: absolute; + width: 1em; + margin-left: -1em; + display: block; + text-decoration: none; + visibility: hidden; + text-align: center; + font-weight: normal; +} + +#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { + content: '\00A7'; + font-size: .85em; + vertical-align: text-top; + display: block; + margin-top: 0.05em; +} + +#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { + visibility: visible; +} + +#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { + color: #578d00; + text-decoration: none; +} + +#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { + color: #74bc00; +} + +.imageblock, .literalblock, .listingblock, .verseblock, .videoblock { + margin-bottom: 1.25em; +} + +.admonitionblock td.content > .title, .exampleblock > .title, .imageblock > .title, .videoblock > .title, .listingblock > .title, .literalblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, .sidebarblock > .title, .tableblock > .title, .verseblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { + text-align: left; + font-weight: bold; +} + +.tableblock > caption { + text-align: left; + font-weight: bold; + white-space: nowrap; + overflow: visible; + max-width: 0; +} + +table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { + font-size: inherit; +} + +.admonitionblock > table { + border: 0; + background: none; + width: 100%; +} + +.admonitionblock > table td.icon { + text-align: center; + width: 80px; +} + +.admonitionblock > table td.icon img { + max-width: none; +} + +.admonitionblock > table td.icon .title { + font-weight: bold; + text-transform: uppercase; +} + +.admonitionblock > table td.content { + padding-left: 1.125em; + padding-right: 1.25em; + border-left: 1px solid #e3e3e2; + color: #6f6f6f; +} + +.admonitionblock > table td.content > :last-child > :last-child { + margin-bottom: 0; +} + +.exampleblock > .content { + border-style: solid; + border-width: 1px; + border-color: #e6e6e6; + margin-bottom: 1.25em; + padding: 1.25em; + background: white; + -webkit-border-radius: 4px; + border-radius: 4px; +} + +.exampleblock > .content > :first-child { + margin-top: 0; +} + +.exampleblock > .content > :last-child { + margin-bottom: 0; +} + +.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6, .exampleblock > .content p { + color: #1b1a19; +} + +.exampleblock > .content h1, .exampleblock > .content h2, .exampleblock > .content h3, .exampleblock > .content #toctitle, .sidebarblock.exampleblock > .content > .title, .exampleblock > .content h4, .exampleblock > .content h5, .exampleblock > .content h6 { + line-height: 1; + margin-bottom: 0.625em; +} + +.exampleblock > .content h1.subheader, .exampleblock > .content h2.subheader, .exampleblock > .content h3.subheader, .exampleblock > .content .subheader#toctitle, .sidebarblock.exampleblock > .content > .subheader.title, .exampleblock > .content h4.subheader, .exampleblock > .content h5.subheader, .exampleblock > .content h6.subheader { + line-height: 1.4; +} + +.exampleblock.result > .content { + -webkit-box-shadow: 0 1px 8px #d9d9d9; + box-shadow: 0 1px 8px #d9d9d9; +} + +.sidebarblock { + border-style: solid; + border-width: 1px; + border-color: #d9d9d9; + margin-bottom: 1.25em; + padding: 1.25em; + background: #f2f2f2; + -webkit-border-radius: 4px; + border-radius: 4px; +} + +.sidebarblock > :first-child { + margin-top: 0; +} + +.sidebarblock > :last-child { + margin-bottom: 0; +} + +.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6, .sidebarblock p { + color: #1b1a19; +} + +.sidebarblock h1, .sidebarblock h2, .sidebarblock h3, .sidebarblock #toctitle, .sidebarblock > .content > .title, .sidebarblock h4, .sidebarblock h5, .sidebarblock h6 { + line-height: 1; + margin-bottom: 0.625em; +} + +.sidebarblock h1.subheader, .sidebarblock h2.subheader, .sidebarblock h3.subheader, .sidebarblock .subheader#toctitle, .sidebarblock > .content > .subheader.title, .sidebarblock h4.subheader, .sidebarblock h5.subheader, .sidebarblock h6.subheader { + line-height: 1.4; +} + +.sidebarblock > .content > .title { + color: #578d00; + margin-top: 0; + line-height: 1.6; +} + +.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { + margin-bottom: 0; +} + +.literalblock > .content pre, .listingblock > .content pre { + background: none; + border-width: 2px 0; + border-style: dotted; + border-color: #74bc00; + -webkit-border-radius: 4px; + border-radius: 4px; + padding: 0.75em 0.75em 0.5em 0.75em; + word-wrap: break-word; +} + +.literalblock > .content pre.nowrap, .listingblock > .content pre.nowrap { + overflow-x: auto; + white-space: pre; + word-wrap: normal; +} + +.literalblock > .content pre > code, .listingblock > .content pre > code { + display: block; +} + +@media only screen { + .literalblock > .content pre, .listingblock > .content pre { + font-size: 0.8em; + } +} + +@media only screen and (min-width: 768px) { + .literalblock > .content pre, .listingblock > .content pre { + font-size: 0.9em; + } +} + +@media only screen and (min-width: 1280px) { + .literalblock > .content pre, .listingblock > .content pre { + font-size: 1em; + } +} + +.listingblock > .content { + position: relative; +} + +.listingblock:hover code[class*=" language-"]:before { + text-transform: uppercase; + font-size: 0.9em; + color: #999; + position: absolute; + top: 0.375em; + right: 0.375em; +} + +.listingblock:hover code.asciidoc:before { + content: "asciidoc"; +} + +.listingblock:hover code.clojure:before { + content: "clojure"; +} + +.listingblock:hover code.css:before { + content: "css"; +} + +.listingblock:hover code.groovy:before { + content: "groovy"; +} + +.listingblock:hover code.html:before { + content: "html"; +} + +.listingblock:hover code.java:before { + content: "java"; +} + +.listingblock:hover code.javascript:before { + content: "javascript"; +} + +.listingblock:hover code.python:before { + content: "python"; +} + +.listingblock:hover code.ruby:before { + content: "ruby"; +} + +.listingblock:hover code.scss:before { + content: "scss"; +} + +.listingblock:hover code.xml:before { + content: "xml"; +} + +.listingblock:hover code.yaml:before { + content: "yaml"; +} + +.listingblock.terminal pre .command:before { + content: attr(data-prompt); + padding-right: 0.5em; + color: #999; +} + +.listingblock.terminal pre .command:not([data-prompt]):before { + content: '$'; +} + +table.pyhltable { + border: 0; + margin-bottom: 0; +} + +table.pyhltable td { + vertical-align: top; + padding-top: 0; + padding-bottom: 0; +} + +table.pyhltable td.code { + padding-left: .75em; + padding-right: 0; +} + +.highlight.pygments .lineno, table.pyhltable td:not(.code) { + color: #999; + padding-left: 0; + padding-right: .5em; + border-right: 1px solid #dddddd; +} + +.highlight.pygments .lineno { + display: inline-block; + margin-right: .25em; +} + +table.pyhltable .linenodiv { + background-color: transparent !important; + padding-right: 0 !important; +} + +.quoteblock { + margin: 0 0 1.25em; + padding: 0.5625em 1.25em 0 1.1875em; + border-left: 1px solid #dddddd; +} + +.quoteblock blockquote { + margin: 0 0 1.25em 0; + padding: 0 0 0.5625em 0; + border: 0; +} + +.quoteblock blockquote > .paragraph:last-child p { + margin-bottom: 0; +} + +.quoteblock .attribution { + margin-top: -.25em; + padding-bottom: 0.5625em; + font-size: inherit; + color: #3d3a37; +} + +.quoteblock .attribution br { + display: none; +} + +.quoteblock .attribution cite { + display: block; + margin-bottom: 0.625em; +} + +table thead th, table tfoot th { + font-weight: bold; +} + +table.tableblock.grid-all { + border-collapse: separate; + border-spacing: 1px; + -webkit-border-radius: 4px; + border-radius: 4px; + border-top: 1px solid #e3e3e2; + border-bottom: 1px solid #e3e3e2; +} + +table.tableblock.frame-topbot, table.tableblock.frame-none { + border-left: 0; + border-right: 0; +} + +table.tableblock.frame-sides, table.tableblock.frame-none { + border-top: 0; + border-bottom: 0; +} + +table.tableblock td .paragraph:last-child p, table.tableblock td > p:last-child { + margin-bottom: 0; +} + +th.tableblock.halign-left, td.tableblock.halign-left { + text-align: left; +} + +th.tableblock.halign-right, td.tableblock.halign-right { + text-align: right; +} + +th.tableblock.halign-center, td.tableblock.halign-center { + text-align: center; +} + +th.tableblock.valign-top, td.tableblock.valign-top { + vertical-align: top; +} + +th.tableblock.valign-bottom, td.tableblock.valign-bottom { + vertical-align: bottom; +} + +th.tableblock.valign-middle, td.tableblock.valign-middle { + vertical-align: middle; +} + +p.tableblock.header { + color: #1b1a19; + font-weight: bold; +} + +td > div.verse { + white-space: pre; +} + +ol { + margin-left: 1.75em; +} + +ul li ol { + margin-left: 1.5em; +} + +dl dd { + margin-left: 1.125em; +} + +dl dd:last-child, dl dd:last-child > :last-child { + margin-bottom: 0; +} + +ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { + margin-bottom: 0.625em; +} + +ul.unstyled, ol.unnumbered, ul.checklist, ul.none { + list-style-type: none; +} + +ul.unstyled, ol.unnumbered, ul.checklist { + margin-left: 0.625em; +} + +ul.checklist li > p:first-child > i[class^="icon-check"]:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child { + margin-right: 0.25em; +} + +ul.checklist li > p:first-child > input[type="checkbox"]:first-child { + position: relative; + top: 1px; +} + +ul.inline { + margin: 0 auto 0.625em auto; + margin-left: -1.375em; + margin-right: 0; + padding: 0; + list-style: none; + overflow: hidden; +} + +ul.inline > li { + list-style: none; + float: left; + margin-left: 1.375em; + display: block; +} + +ul.inline > li > * { + display: block; +} + +.unstyled dl dt { + font-weight: normal; + font-style: normal; +} + +ol.arabic { + list-style-type: decimal; +} + +ol.decimal { + list-style-type: decimal-leading-zero; +} + +ol.loweralpha { + list-style-type: lower-alpha; +} + +ol.upperalpha { + list-style-type: upper-alpha; +} + +ol.lowerroman { + list-style-type: lower-roman; +} + +ol.upperroman { + list-style-type: upper-roman; +} + +ol.lowergreek { + list-style-type: lower-greek; +} + +.hdlist > table, .colist > table { + border: 0; + background: none; +} + +.hdlist > table > tbody > tr, .colist > table > tbody > tr { + background: none; +} + +td.hdlist1 { + padding-right: .8em; + font-weight: bold; +} + +td.hdlist1, td.hdlist2 { + vertical-align: top; +} + +.literalblock + .colist, .listingblock + .colist { + margin-top: -0.5em; +} + +.colist > table tr > td:first-of-type { + padding: 0 .8em; + line-height: 1; +} + +.colist > table tr > td:last-of-type { + padding: 0.25em 0; +} + +.qanda > ol > li > p > em:only-child { + color: #00467f; +} + +.thumb, .th { + line-height: 0; + display: inline-block; + border: solid 4px white; + -webkit-box-shadow: 0 0 0 1px #dddddd; + box-shadow: 0 0 0 1px #dddddd; +} + +.imageblock.left, .imageblock[style*="float: left"] { + margin: 0.25em 0.625em 1.25em 0; +} + +.imageblock.right, .imageblock[style*="float: right"] { + margin: 0.25em 0 1.25em 0.625em; +} + +.imageblock > .title { + margin-bottom: 0; +} + +.imageblock.thumb, .imageblock.th { + border-width: 6px; +} + +.imageblock.thumb > .title, .imageblock.th > .title { + padding: 0 0.125em; +} + +.image.left, .image.right { + margin-top: 0.25em; + margin-bottom: 0.25em; + display: inline-block; + line-height: 0; +} + +.image.left { + margin-right: 0.625em; +} + +.image.right { + margin-left: 0.625em; +} + +a.image { + text-decoration: none; +} + +span.footnote, span.footnoteref { + vertical-align: super; + font-size: 0.875em; +} + +span.footnote a, span.footnoteref a { + text-decoration: none; +} + +#footnotes { + padding-top: 0.75em; + padding-bottom: 0.75em; + margin-bottom: 0.625em; +} + +#footnotes hr { + width: 20%; + min-width: 6.25em; + margin: -.25em 0 .75em 0; + border-width: 1px 0 0 0; +} + +#footnotes .footnote { + padding: 0 0.375em; + line-height: 1.3; + font-size: 0.875em; + margin-left: 1.2em; + text-indent: -1.2em; + margin-bottom: .2em; +} + +#footnotes .footnote a:first-of-type { + font-weight: bold; + text-decoration: none; +} + +#footnotes .footnote:last-of-type { + margin-bottom: 0; +} + +#content #footnotes { + margin-top: -0.625em; + margin-bottom: 0; + padding: 0.75em 0; +} + +.gist .file-data > table { + border: none; + background: #fff; + width: 100%; + margin-bottom: 0; +} + +.gist .file-data > table td.line-data { + width: 99%; +} + +div.unbreakable { + page-break-inside: avoid; +} + +.big { + font-size: larger; +} + +.small { + font-size: smaller; +} + +.underline { + text-decoration: underline; +} + +.overline { + text-decoration: overline; +} + +.line-through { + text-decoration: line-through; +} + +.aqua { + color: #00bfbf; +} + +.aqua-background { + background-color: #00fafa; +} + +.black { + color: black; +} + +.black-background { + background-color: black; +} + +.blue { + color: #0000bf; +} + +.blue-background { + background-color: #0000fa; +} + +.fuchsia { + color: #bf00bf; +} + +.fuchsia-background { + background-color: #fa00fa; +} + +.gray { + color: #606060; +} + +.gray-background { + background-color: #7d7d7d; +} + +.green { + color: #006000; +} + +.green-background { + background-color: #007d00; +} + +.lime { + color: #00bf00; +} + +.lime-background { + background-color: #00fa00; +} + +.maroon { + color: #600000; +} + +.maroon-background { + background-color: #7d0000; +} + +.navy { + color: #000060; +} + +.navy-background { + background-color: #00007d; +} + +.olive { + color: #606000; +} + +.olive-background { + background-color: #7d7d00; +} + +.purple { + color: #600060; +} + +.purple-background { + background-color: #7d007d; +} + +.red { + color: #bf0000; +} + +.red-background { + background-color: #fa0000; +} + +.silver { + color: #909090; +} + +.silver-background { + background-color: #bcbcbc; +} + +.teal { + color: #006060; +} + +.teal-background { + background-color: #007d7d; +} + +.white { + color: #bfbfbf; +} + +.white-background { + background-color: #fafafa; +} + +.yellow { + color: #bfbf00; +} + +.yellow-background { + background-color: #fafa00; +} + +span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { + cursor: default; +} + +.admonitionblock td.icon [class^="icon-"]:before { + font-size: 2.5em; + text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.5); + cursor: default; +} + +.admonitionblock td.icon .icon-note:before { + content: "\f05a"; + color: #005498; + color: #003f72; +} + +.admonitionblock td.icon .icon-tip:before { + content: "\f0eb"; + text-shadow: 1px 1px 2px rgba(155, 155, 0, 0.8); + color: #111; +} + +.admonitionblock td.icon .icon-warning:before { + content: "\f071"; + color: #bf6900; +} + +.admonitionblock td.icon .icon-caution:before { + content: "\f06d"; + color: #bf3400; +} + +.admonitionblock td.icon .icon-important:before { + content: "\f06a"; + color: #bf0000; +} + +.conum { + display: inline-block; + color: white !important; + background-color: #222222; + -webkit-border-radius: 100px; + border-radius: 100px; + text-align: center; + width: 20px; + height: 20px; + font-size: 12px; + font-weight: bold; + line-height: 20px; + font-family: Arial, sans-serif; + font-style: normal; + position: relative; + top: -2px; + letter-spacing: -1px; +} + +.conum * { + color: white !important; +} + +.conum + b { + display: none; +} + +.conum:after { + content: attr(data-value); +} + +.conum:not([data-value]):empty { + display: none; +} + +.literalblock > .content > pre, .listingblock > .content > pre { + -webkit-border-radius: 0; + border-radius: 0; +} diff --git a/manual/src/main/assembly/manual.xml b/manual/src/main/assembly/manual.xml new file mode 100644 index 0000000000..44378a4629 --- /dev/null +++ b/manual/src/main/assembly/manual.xml @@ -0,0 +1,18 @@ + + manual + + zip + + + + ${project.build.directory}/generated-docs + . + + **/* + images/** + + + + diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000000..47222cc518 --- /dev/null +++ b/pom.xml @@ -0,0 +1,884 @@ + + 4.0.0 + + com.buschmais.jqassistant + parent + 2.4.0-SNAPSHOT + pom + + jQAssistant Parent + https://jqassistant.org/ + + + Maven Parent POM for all projects of the jQAssistant ecosystem. + It provides common and useful settings shared by all + jQAssistant projects. + + + + jQAssistant Development Team + https://jqassistant.org + + + + + GNU General Public License, v3 + https://www.gnu.org/licenses/gpl-3.0.html + + + + + scm:git:https://github.com/jqassistant/jqassistant.git + scm:git:https://github.com/jqassistant/jqassistant.git + https://github.com/jqassistant/jqassistant/ + parent-2.4.0-M1 + + + + 3.6 + + + + + sonatype-nexus-snapshots + Sonatype Nexus Snapshots + https://oss.sonatype.org/content/repositories/snapshots/ + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + dirk-mahler + Dirk Mahler + dirk.mahler@buschmais.com + + + stephan.pirnbaum + Stephan Pirnbaum + stephan.pirnbaum@buschmais.com + + + oliver.b.fischer + Oliver B. Fischer + o.b.fischer@swe-blog.net + + + + + + 3.22.0 + 9.7 + 3.1.6 + 1.9.4 + 1.15 + 3.2.2 + 1.8 + 2.16.1 + 2.6 + 3.14.0 + 1.12.0 + 32.0.1-jre + 7.0.0 + 2.0.0.0 + 4.4.16 + 2.17.2 + 2.4.0-b180830.0359 + 2.4.0-b180830.0438 + 2.1.1 + 1.3.2 + 1.0.36 + 5.10.3 + 2.2.0 + 1.18.32 + 1.5.5.Final + 2.23.4 + 4.4.35 + 4.4.0.29 + 5.21.2 + ${neo4j_5x.version} + + ${neo4j_4x.version} + 4.4.17 + 5.21.0 + 1.7.36 + 3.8.2 + 1.9.21 + 2.0 + 2.3 + 2.0.1.Final + 2.2.7 + + 1.12.0 + 1.18.20.0 + 3.3.0 + 3.6.0 + 3.1.0 + 9.0.8 + 2.8.0 + 3.1.0 + 3.13.0 + 3.3.0 + 3.1.2 + 3.0.1 + 3.0.0 + 3.3.1 + 0.8.12 + 3.1.1 + 3.2.2 + 3.8.0 + 0.15.1 + 3.3.0 + 3.1.1 + 3.12.1 + 3.3.1 + ${maven.failsafe.version} + 1.0.2 + 3.7.0 + 2.2.4 + 1.6 + 1.6.13 + 4.0.0.4121 + + 3.5.0 + UTF-8 + UTF-8 + + memory:/// + + 2.3.1 + + 2.4.0-M1 + + + + + + + org.asciidoctor + asciidoctor-maven-plugin + ${org.asciidoctor-plugin.version} + + + com.buschmais.jqassistant + jqassistant-maven-plugin + 2.4.0-M1 + + + default + + scan + analyze + + + + + + jqassistant: + scan: + include: + files: + - ${project.basedir}/readme.adoc + - ${project.basedir}/src/main/asciidoc + - ${project.basedir}/.editorconfig + - ${project.basedir}/LICENSE + analyze: + groups: + - jqa-* + + + + + com.buschmais.jqassistant.build + jqassistant + ${jqassistant.build.version} + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven.checkstyle.version} + + + + check + + + + + checkstyle/jqa-code-style.xml + checkstyle/jqa-code-suppressions.xml + + ${project.build.sourceDirectory} + + true + + ${project.build.testSourceDirectory} + + true + + + + com.buschmais.jqassistant.build + checkstyle + ${jqassistant.build.version} + + + + + org.apache.maven.plugins + maven-clean-plugin + ${maven.clean.version} + + + + org.apache.maven.plugins + maven-failsafe-plugin + ${maven.failsafe.version} + + + + ${it.jqassistant.store.uri} + + + + @{argLine} + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.nio=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven.surefire.version} + + + + ${it.jqassistant.store.uri} + + + + @{argLine} + --add-opens java.base/java.lang=ALL-UNNAMED + --add-opens java.base/java.nio=ALL-UNNAMED + --add-opens java.base/java.util=ALL-UNNAMED + + + + + org.jacoco + jacoco-maven-plugin + ${maven.jacoco.version} + + + org.apache.maven.plugins + maven-invoker-plugin + ${maven.invoker.version} + + ${project.build.directory}/it + ${project.build.directory}/it-repo + false + + 11 + + + verify -e + + true + verify + true + + + + integration-test + + install + run + + + + + + org.apache.groovy + groovy + 4.0.22 + + + org.apache.groovy + groovy-xml + 4.0.22 + + + + + org.apache.maven.plugins + maven-assembly-plugin + ${maven.assembly.version} + + + attach-asciidoc + package + + single + + + + asciidoc + + + + + + + com.buschmais.jqassistant.build + asciidoc + ${jqassistant.build.version} + + + + + + org.projectlombok + lombok-maven-plugin + ${lombok-maven-plugin.version} + + + generate-sources + + delombok + + + true + false + ${project.basedir}/src/main/java + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.version} + + UTF-8 + 11 + + + org.projectlombok + lombok + ${lombok.version} + + + org.projectlombok + lombok-mapstruct-binding + 0.2.0 + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + + + -Amapstruct.unmappedTargetPolicy=ERROR + -Amapstruct.unmappedSourcePolicy=ERROR + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.version} + + UTF-8 + false + none + + ${project.basedir}/src/main/java;${project.build.directory}/generated-sources/delombok;${project.build.directory}/generated-sources/xjc + + + + + attach-javadocs + + jar + + + + + + + org.apache.maven.plugins + maven-install-plugin + ${maven.install.version} + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.version} + + + + org.jvnet.jaxb2.maven2 + maven-jaxb2-plugin + ${maven.jaxb2-plugin.version} + + + + generate + + + src/main/resources/META-INF/xsd + + false + + -mark-generated + -Xdefault-value + + + + org.jvnet.jaxb2_commons + jaxb2-default-value + 1.1 + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + ${maven.shade.version} + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven.dependency.version} + + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven.deploy.version} + + + + org.apache.maven.plugins + maven-release-plugin + ${maven.release.version} + + forked-path + true + @{version} + deploy + release + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + ${maven.sonar.version} + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven.enforcer.version} + + + enforce-plugin-versions + + enforce + + + + + [${maven.min.version},) + + + + org.apache.maven.plugins:maven-surefire-plugin + org.apache.maven.plugins:maven-failsafe-plugin + org.apache.maven.plugins:maven-surefire-report-plugin + + + + Best Practice is to always define plugin versions! + true + true + true + clean,deploy,site + + + + + + enforce-java-version + + enforce + + + + + [11,) + + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven.resources.version} + + UTF-8 + + + + + org.apache.maven.plugins + maven-site-plugin + ${maven.site.version} + + + org.apache.maven.doxia + doxia-module-markdown + ${doxia-markdown.version} + + + org.asciidoctor + asciidoctor-maven-plugin + ${org.asciidoctor-plugin.version} + + + + true + + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven.source.plugin} + + + attach-sources + + jar-no-fork + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + ${maven.buildhelper.version} + + + + org.codehaus.mojo + xml-maven-plugin + ${maven.xml.version} + + + + org.cyclonedx + cyclonedx-maven-plugin + ${maven.cyclonedx.version} + + + package + + makeAggregateBom + + + true + false + true + true + false + + + + + + org.owasp + dependency-check-maven + ${maven.owasp-dependency-check.version} + + true + true + + false + + CSV + XML + HTML + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${nexus-staging-maven-plugin.version} + + + + + + + org.projectlombok + lombok-maven-plugin + + + org.apache.maven.plugins + maven-enforcer-plugin + + + org.jacoco + jacoco-maven-plugin + + + + prepare-agent + report + + + + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + com.buschmais.jqassistant + jqassistant-maven-plugin + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + + + + neo4jv4 + + 11 + + + ${neo4j_4x.version} + + + + neo4jv5 + + [17,) + + + ${neo4j_5x.version} + + + + IT + + + + org.apache.maven.plugins + maven-failsafe-plugin + + + + integration-test + verify + + + + + + + + + it-store-remote + + neo4j://localhost:7687 + + + + it-store-file + + file:${project.build.directory}/jqassistant/test-store + + + + owasp + + + + org.owasp + dependency-check-maven + ${maven.owasp-dependency-check.version} + + + + check + + + + + + + + + sonar + + + performRelease + true + + + + https://sonarcloud.io + jqassistant + jqassistant_jqassistant + ${env.SONARCLOUD_LOGIN} + + + + + org.sonarsource.scanner.maven + sonar-maven-plugin + + + verify + + sonar + + + + + + + + + release + + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + + + enforce-release-versions + + enforce + + + + + + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + + + + --pinentry-mode + loopback + + + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + true + + sonatype-nexus-staging + https://oss.sonatype.org/ + true + + + + + + + IntelliJ + + false + + idea.maven.embedder.version + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + + + + bom + core + plugin + distribution-specification + cli + maven + manual + +