ABONAMENTE VIDEO REDACȚIA
RO
EN
NOU
Numărul 148
Numărul 147 Numărul 146 Numărul 145 Numărul 144 Numărul 143 Numărul 142 Numărul 141 Numărul 140 Numărul 139 Numărul 138 Numărul 137 Numărul 136 Numărul 135 Numărul 134 Numărul 133 Numărul 132 Numărul 131 Numărul 130 Numărul 129 Numărul 128 Numărul 127 Numărul 126 Numărul 125 Numărul 124 Numărul 123 Numărul 122 Numărul 121 Numărul 120 Numărul 119 Numărul 118 Numărul 117 Numărul 116 Numărul 115 Numărul 114 Numărul 113 Numărul 112 Numărul 111 Numărul 110 Numărul 109 Numărul 108 Numărul 107 Numărul 106 Numărul 105 Numărul 104 Numărul 103 Numărul 102 Numărul 101 Numărul 100 Numărul 99 Numărul 98 Numărul 97 Numărul 96 Numărul 95 Numărul 94 Numărul 93 Numărul 92 Numărul 91 Numărul 90 Numărul 89 Numărul 88 Numărul 87 Numărul 86 Numărul 85 Numărul 84 Numărul 83 Numărul 82 Numărul 81 Numărul 80 Numărul 79 Numărul 78 Numărul 77 Numărul 76 Numărul 75 Numărul 74 Numărul 73 Numărul 72 Numărul 71 Numărul 70 Numărul 69 Numărul 68 Numărul 67 Numărul 66 Numărul 65 Numărul 64 Numărul 63 Numărul 62 Numărul 61 Numărul 60 Numărul 59 Numărul 58 Numărul 57 Numărul 56 Numărul 55 Numărul 54 Numărul 53 Numărul 52 Numărul 51 Numărul 50 Numărul 49 Numărul 48 Numărul 47 Numărul 46 Numărul 45 Numărul 44 Numărul 43 Numărul 42 Numărul 41 Numărul 40 Numărul 39 Numărul 38 Numărul 37 Numărul 36 Numărul 35 Numărul 34 Numărul 33 Numărul 32 Numărul 31 Numărul 30 Numărul 29 Numărul 28 Numărul 27 Numărul 26 Numărul 25 Numărul 24 Numărul 23 Numărul 22 Numărul 21 Numărul 20 Numărul 19 Numărul 18 Numărul 17 Numărul 16 Numărul 15 Numărul 14 Numărul 13 Numărul 12 Numărul 11 Numărul 10 Numărul 9 Numărul 8 Numărul 7 Numărul 6 Numărul 5 Numărul 4 Numărul 3 Numărul 2 Numărul 1
×
▼ LISTĂ EDIȚII ▼
Numărul 31
Abonament PDF

Convergența documentației într-un proiect software multimodular

Alexandru Albu
Software Architect @ Bosch



PROGRAMARE


Documentele explicative ale produselor software sunt folosite ca manuale de referință pentru proiectanții interfețelor utilizator, pentru programatorii care scriu codul și pentru testerii care se asigură că produsul funcționează corect.

Într-o aplicație multimodulară, fiecare componentă este dezvoltată și lansată independent. Păstrarea documentației actualizate pentru fiecare componentă nu este ușoară deoarece nu totul ține doar de redactarea ei, ci și de centralizarea tuturor documentelor în așa fel încât să fie găsite ușor de către persoanele interesate.

Context

Scopul articolului de față este prezentarea unei abordări pentru simplificarea acestui proces, bazându-se pe Build Automation, care să colecteze și să publice documentele.

Vom trece împreună prin procesul de configurare al artifactelor Apache Maven, al serverului Jenkins CI, pentru ca în final vom ajunge la crearea unui proiect ce regenerează un website care reflectă starea actuală a întregului proiect, oferind acces la toate documentele disponibile dintr-un singur loc.

Studiul nostru de caz este un framework scris în limbajul de programare Java, care este format din mai multe module Maven. Fiecare modul conține unul sau multe documente scrise în Markdown. Documentele sunt scrise de către programatori, iar acestea pot fi readme-uri, howto-uri, documentații tehnice și altele.

Ce vrem să obținem?

Înainte să începem, va trebui să ne imaginăm cum va arăta soluția finală. Ne dorim o pagină html frontală care să afișeze cele mai recente documente scrise de către programatori pentru modulele lansate.

Livrarea împreună a release note-urilor și a altor documente cu produsul software și JavaDoc reprezintă o practică obișnuită. Ele însoțesc produsul, iar cu ajutorul lor, consumatorii produsului îl pot folosi și înțelege corect.

Prin lansarea artifactelor Maven, fișierele compilate ajung într-un depozit (Repository) de cod structurat. În acest depozit pot ajunge nu doar fișierele compilate, ci și JavaDoc-ul, codul sursă sau un întreg website ce prezintă informații despre dependențele proiectului, informații despre issue tracking, integrare continuă (CI), echipă și multe altele.

Website- ul proiectului este cel mai important element în atingerea scopului nostru, pentru că acesta va agrega toate legăturile spre _website-_urile fiecărui modul într-o singură pagină html.

Imaginați-vă că pagina html arată în felul următor:

  <div>
    <h1>module1</h1>
    <a href=”modules/module1/index.html”>About</a>
    <a href=”modules/module1/Readme.html”>Readme</a>
    <a href=”modules/module1/ReleaseNotes.html”>Release Notes</a>
 </div>
 <div>
    <h1>module2</h1>
    <a href=”modules/module2/index.html”>About</a>
    <a href=”modules/module2/Readme.html”>Readme</a>
    <a href=”modules/module2/ReleaseNotes.html”>Release Notes</a>
 </div>

Acest model presupune prezența fișierelor referite pe disc, acestea fiind generate în timpul procesului.

Abordare

Modulele noastre sunt artifacte Maven, iar abordarea este bazată pe Maven Build Lifecycle.

Generarea site-ului unui modul

Generăm _site-_ul folosind maven-site-plugin. Folosind doar acest plugin putem genera elementele implicite, cum sunt Project Summary, Project Plugins, Dependencies și altele.

Cu puțină configurare și ajutor, putem interveni în fluxul normal al _plugin-_ului. În acest mod putem include fișierele noastre Markdown ca fișiere html, referite din _website-_ul generat.

Ca să transformăm fișierele Markdown în html, avem nevoie de d_oxia-module-markdown_ca dependență pentru acestplugin. Folosind acesta, procesul de generare al website-ului se uită în interiorul directorului src/site/markdown și convertește fiecare fișier cu extensia .md într-un fișier html.

Această etapă poate părea simplă, dar dacă fișierele noastre *.md conțin imagini, acestea sunt pur și simplu trecute cu vederea de către plugin. Acest plugin doar copiază conținutul directorului src/site/resources.

De asemenea, noi dorim ca fișierele noastre Markdown să fie accesibile programatorilor și în modul offline, ei putând oricând arunca o privire peste ele. Referirea imaginilor din directorul src/site/resources va funcționa doar în modul offline, deoarece în urma generării _website-_ului directorul resources nu va mai fi prezent, iar astfel vom avea parte de referințe defecte către imagini.Ideal ar fi să avem toate fișierele Markdown în directorul src/site/resources, referind imaginilie din src/site/resources/images, fiind mai simplu din images (ca director relativ) deoarece după generarea_website-_ului conținutul directorului images este contopit cu celelalte imagini pe care _plugin-_ul le generează în directorul target/site/images.

În concluzie, directorul src al unui modul are următoarea structură:

În interiorul fișierului Readme.md găsim referințele spre imagini:

![Alternative text]

   (images/image1.jpg „Text descriptiv”)

După ce _website—_ul este generat, ne așteptăm să găsim fișierele noastre în directorul target astfel:

Cele două fișiere html ar trebui să fie referite în cadrul fișierelor html generate de către plugin. Pentru a realiza acest lucru, avem nevoie să-i comunicăm lui Maven următoarele instrucțiuni:

  1. Înainte de a genera _website-ul, copiați toate fișierele *.md_ din src/site/resources în src/site/markdown. Atenție, directorul nou poate fi inexistent.

  2. Generați _website-_ul pentru proiect, dar în rezultatul final este de preferat să existe referințe către documentele generate pe baza fișierelor Markdown. Avem un fișier Readme.md și unul ReleaseNotes.md, deci ar trebui să facă referire la fișierele Readme.html și ReleaseNotes.html.

  3. După ce ați terminat de generat, ștergeți directorul src/site/markdown. Codul trebuie să rămână curat, fără fișiere duplicate.

Începem cu cel de-al doilea punct, prin descrierea referințelor spre viitoarele fișiere html în cadrul src/site/site.xml:

<?xml version=”1.0” encoding=”UTF-8”?>

<project xmlns=”http://maven.apache.org/DECORATION/1.4.0” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://maven.apache.org/DECORATION/1.4.0 [http://maven.apache.org/xsd/decoration-1.4.0.xsd](http://maven.apache.org/xsd/decoration-1.4.0.xsd) [http://maven.apache.org/DECORATION/1.4.0](http://maven.apache.org/DECORATION/1.4.0) „>

<!-- temă, reclame și alte configurări ale site-ului -->

<body>
<!—- meniu pentru documentele programatorilor -->
<menu name=”Developer documents”>
 <item name=”Readme” href=”Readme.html”/>
 <item name=”ReleaseNotes” href=”ReleaseNotes.html”/>
</menu>

 <!-- meniu pentru javadoc, jxr, și altele -->

  <menu ref=”reports”/>
</body>
</project>

Plugin- ul citește aceste instrucțiuni și generează _website-_ul în concordanță. Am adăugat un nou meniu ce referă documentele noastre.

Primul și ultimul punct implică manipulare de fișiere, iar acest lucru este o sarcină perfectă pentru un task Ant. În Maven putem folosi maven-antrun-plugin pe care îl configurăm să execute două sarcini:

Mai jos este prezentat fișierul pom.xml rezultat:

    <project .. >
        <dependencies>
            ...
        </dependencies>
        <build>
      <!—- pregătește fișierele Markdown pentru Maven Site-->
        <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-antrun-plugin</artifactId>
            <executions>
               <execution>
                  <id>pre-markdown</id>
                  <phase>pre-site</phase>
                  <configuration>
                    <tasks>
                    <delete dir=”${project.basedir}/src/site/markdown” />
                    <mkdir dir=”${project.basedir}/src/site/markdown” />
                    <copy todir=”${project.basedir}/src/site/markdown”>
        <fileset dir=”${project.basedir}/
            src/site/resources” includes=”**/*.md” />
                    </copy>
                   </tasks>
                 </configuration>
                 <goals>
                   <goal>run</goal>
                 </goals>
               </execution>
                 <execution>
                   <id>post-markdown</id>
                   <phase>site</phase>
                   <configuration>
                     <tasks>
                     <delete dir=”${project.basedir}/src/site/markdown” />
                     </tasks>
                   </configuration>
                    <goals>
                     <goal>run</goal>
                    </goals>
                   </execution>
                </executions>
            </plugin>

            <!—generatorul de site e legat de  sectiunea de raportare -->

        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.3</version>
            <dependencies>
            <dependency>
            <groupId>org.apache.maven.doxia</groupId>
            <artifactId>doxia-module-markdown
            </artifactId>
            <version>1.5</version>
            </dependency>
           </dependencies>
         </plugin>
        </build>

        <reporting>
      <outputDirectory>${project.build.directory}/site</outputDirectory>

<!—configurare pentru alte lucruri legate de raportare, cum sunt maven-javadoc-plugin, maven-jxr-plugin -->

     </reporting>
    </project>

Cu ajutorul comenzii mvn site website-ul dorit va fi generat în directorul target/site.

Deploy-ul site-ului unui modul

Următoarea fază în atingerea scopului nostru este să împachetăm website- ul generat într-un fișier jar care să fie încărcat în Maven Repository.

Plugin- ul site știe cum să creeze un fișier jar pe baza fișierelor din directorul target/site. Tot ce trebuie să facem este să apelăm mvn site:jar, dar cu o singură remarcă: faza pre-site este executată doar dacă apelăm mvn site, fără scopul: jar. Pentru a fi siguri că fișierele Markdown sunt luate în considerare chiar și când directorul target este gol sau inexistent, ar trebui să apelăm mvn site site:jar.

Rezultatul este un fișier jar nou, target/module1-site.jar. Ca să putem considera acest pas complet, mai trebuie să încărcăm acest fișier jar în Maven Repository. Acest lucru este posibil cu ajutorul Maven Deploy Plugin.

Proiectul resurselor

Scopul acestui proiect este agregarea tuturor resurselor disponibile într-un singur website. Pe lângă documentațiile modulelor, el poate ține și documente generale, cum ar fi primii pași ai programatorilor care intră pe proiect sau documentații tehnice de ansamblu. Pentru acestea, maven-site-plugin poate fi aplicat folosind aceeași manieră ca și în cazul modulelor.

Pentru a descărca website- urile generate folosim Maven Dependency Plugin. Acesta ne ajută să obținem artifactele și fișierele *-site.jar încărcate la pasul anterior. Scopul nostru aici este să dezarhivăm toate aceste fișiere în interiorul directorului target/site/modules, iar astfel putem menține structura dorită pentru website.

Pentru a obține arhivele *-site pentru module, toate trebuie declarate ca dependențe ale proiectului resurselor în fișierul pom.xml:

<project .. >
    <dependencies>
        <!-- module 1 -->
        <!-- module 2 -->
<!-- ... -->
    <dependencies>
    <build>
   <plugins>
     <!-- antrun pentru a genera html suplimentar din markdown -->

<!-- (!) -->

     <!-- groovy plugin pentru a executa operții de intrare/ieșire pe disc, explicate în secțiunile -->

<!-- (!) -->

     <!-- site plugin pentru a genera site-ul proiectului actual -->

     <plugin>
       <groupId>org.apache.maven.plugins</groupId>
       <artifactId>maven-dependency-plugin
       </artifactId>
        <executions>
           <execution>
           <id>sites-modules</id>
           <phase>compile</phase>
           <goals>
             <goal>unpack-dependencies</goal>
           </goals>
           <configuration>
           <classifier>site</classifier>

           <!—acesetea sunt importante, aici se enumeră toate artifactele care sunt biblioteci ale proiectului (module), separate prin virgulă (,) -->

           <includeArtifactIds>module1, module2, ...</includeArtifactIds>
           <failOnMissingClassifierArtifact>false
           </failOnMissingClassifierArtifact>

           <outputDirectory>
           ${project.build.directory}/site/modules
           </outputDirectory>
          <useSubDirectoryPerArtifact>true
          </useSubDirectoryPerArtifact>
         </configuration>
        </execution>
      </executions>
    </plugin>
  </plugins>
 </build>
</project>

Acest plugin va dezarhiva conținutul fiecărui website de modul într-un subdirector separat din cadrul target/site/modules al proiectului resurselor.

Ultima chestiune importantă aici este proiectarea fișierului index.html în așa fel încât el va conține referințe către toate sub-site-urile modulelor. Pentru că modulele noastre sunt versionate, vrem ca proiectul resurselor să-și dea seama singur de căile către sub-site-uri. Dacă facem pagina de index dinamică, putem adăuga foarte ușor un script care populează pagina cu conținutul corespunzător, prin declararea unui vector într-un fișier .js separat, ca și în exemplul de mai jos:

var modules = [
    „module1-1.3-SNAPSHOT-site-jar”,
    „module2-1.5-site-jar”,
    ...
];

Codul JavaScript poate utiliza vectorul modules și introduce următoarele elemente DOM în pagina index:

<div>
   <h1>module1</h1>
   <h2>Version 1.3-SNAPSHOT</h2>
   <a href=”modules/module1-1.3-SNAPSHOT-site-jar/index.html”>About</a>
   <a href=”modules/module1-1.3-SNAPSHOT-site-jar/Readme.html”>Readme</a>
   <a href=”modules/module1-1.3-SNAPSHOT-site-jar/ReleaseNotes.html”>Release Notes</a>
</div>
<div>
   <h1>module2</h1>
   <h2>Version 1.5</h2>
   <a href=”modules/module2-1.5-site-jar/index.html”>About</a>
   <a href=”modules/module2-1.5-site-jar/Readme.html”>Readme</a>
   <a href=”modules/module2-1.5-site-jar/ReleaseNotes.html”>Release Notes</a>
    </div>

Fișierul nostru modules.js este populat în timpul procesului de build al proiectului resurselor cu ajutor din partea groovy-maven-plugin. Scopul acestuia este acela de a executa un cod care iterează prin directoarele din cadrul target/site/modules și imprimă numele lor în fișierul /site/config/modules.js, iar în acest fel noi obținem vectorul de căi spre module. Codul poate fi citit mai jos:

...
<plugin>
  <!—imprimă în config/modules.js numele directoarelor corespunzătoare -->
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>groovy-maven-plugin</artifactId>
  <version>1.5</version>
  <executions>
     <execution>
        <phase>package</phase>
        <goals>
          <goal>execute</goal>
         </goals>
   <configuration>
           <source>
  <![CDATA[println(„==== Creează config/modules.js ====”);
    File modFile = new File(„${project.build.directory}/site/config/modules.js”);
   BufferedWriter modWriter = new BufferedWriter(new FileWriter(modFile));
   modWriter.writeLine(„var modules = [„);new File( „${project.build.directory}/site/modules”).eachDir() { dir -> modWriter.writeLine(„’” + dir.getName() + „’,”);
     }
     modWriter.writeLine(„];”);
     modWriter.close();
    ]]>
           </source>
          </configuration>
        </execution>
       </executions>
    </plugin>
...

Prin apelarea mvn site site:jar în cadrul proiectului resurselor obținem arhiva website- ului dorit. Această arhivă poate fi mai apoi încărcată într-un Server Web HTTP și făcută accesibilă tuturor celor interesați.

Concluzii

Având toate modulele configurate și proiectul resurselor creat, toate comenzile mvn pot fi apelate cu ușurință de către Jenkins CI, iar_website-_ul final poate fi încărcat pe un Server Web HTTP ca pas post build. De fiecare dată când un modul este lansat, sub-website- ul lui este publicat, iar website- ul principal poate fi regenerat. În acest fel ne asigurăm că documentațiile cele mai recente sunt disponibile programatorilor, fără a fi nevoie de niciun efort sau intervenție suplimentară. Toate acestea sunt făcute în spiritul integrării continue.

Conferință TSM

NUMĂRUL 147 - Automotive

Sponsori

  • Accenture
  • BT Code Crafters
  • Accesa
  • Bosch
  • Betfair
  • MHP
  • BoatyardX
  • .msg systems
  • P3 group
  • Ing Hubs
  • Cognizant Softvision
  • Colors in projects