Definition

  • Define dictionary (pairs key-value)

    1
    2
    3
    4
    alien_0 = {
    'color': 'green',
    'points': 2
    }
  • Define empty dictionary

    1
    alien_0 = {}
  • Define ordered dictionary (keep order in which the data was entered)

    1
    2
    3
    4
    5
    from collections import OrderedDict 

    fav_languages = OrderedDict()
    fav_languages['angeles'] = ['java', 'python']
    fav_languages['juan'] = ['php', 'java']
  • Get item

    1
    2
    alien_color = alien_0.get('color')
    alien_points = alien_0.get('points', 0)
  • Get lenth

    1
    num_alien_traits = len(alien0)

Basic modifications

  • Add

    1
    alien_0['speed'] = 1.5 
  • Modify

    1
    2
    3
    4
    5
    alien_0 = {
    'color': 'green',
    'points': 2
    }
    alien_0['color'] = 'purple'
  • Remove

    1
    del alien_0['points']

Iteration

  • Loop through items

    1
    2
    for key, value in alien0.items():
    print(key + ": " + value)
  • Loop through keys

    1
    2
    3
    4
    5
    for key in alien0.keys():
    print("key: " + key)

    for key in sorted(alien0.keys()):
    print("key: " + key)
  • Loop through values

    1
    2
    for value in alien0.values():
    print("value: " + value)

Python lists and tuples

Definition

  • Define mutable list

    1
    2
    3
    4
    # assign
    bikes = ["mountain bike", "motorbike"]
    # clone a list
    copy_of_bikes = bikes[:]
  • Define inmutable lists (tuples)

    1
    2
    3
    dimensions = (800, 600)
    # only overwriting the whole tuple is allowed
    dimensions = (1200, 900)
  • Get item

    1
    2
    3
    4
    # get first
    first_bike = bikes[0]
    # get last
    last_bike = bikes[-1]
  • Pop item (remove after get)

    1
    2
    3
    4
    5
    6
    7
    #pop last
    most_recent_bike = bikes.pop()
    print(most_recent_bike)

    # you can also pop other items
    first_item_in_bikes = bikes.pop(0)
    print(first_item_in_bikes)
  • Get list length

    1
    num_bikes = len(bikes)

Basic modifications

Edit existing elememy

  • Set new value
    1
    2
    3
    bikes = ["mountain bike", "motorbike"]
    bikes[0] = "custom mountain bike"
    bikes[-2] = "old bike"

Add and remove items

  • Generic
    1
    2
    3
    4
    bikes = []
    bikes.append("mountain bike")
    bikes.append("aegean blue")
    bikes.remove("aegean blue")
    • By position
      1
      2
      3
      4
      bikes = []
      bikes.insert(0, "mountain bike")
      bikes.insert(1, "motorbike")
      del bikes[-1]

Advanced modification

Sort

  • Permanent sort
    1
    2
    3
    4
    # in alphabetical order
    bikes.sort()
    # in reverse alphabetical order
    bikes.sort(reverse=True)
  • Temporal sort
    1
    2
    3
    4
    # in alphabetical order
    print(sorted(bikes))
    # in reverse alphabetical order
    print(sorted(bikes, reverse=True))
  • Reverse order
    1
    bikes.reverse() 

Iteration

  • Loops

    • Simple loop
      1
      2
      3
      upper_case_bikes = []
      for bike in bikes:
      upper_names.append(bike.upper())
    • Expression compression
      1
      upper_case_bikes = [bike.upper() for bike in bikes]
  • Range

    • Print a range
      1
      2
      3
      4
      5
      6
      #  Implicit: from 1 to 1000
      for number in range(1001):
      print(number)
      # Explicit: from 1 to 1000
      for number in range(1, 1001):
      print(number)
    • Expression compression
      1
      double_values = [x*2 for x in range(1, 11)]

Simple statistics

  • Minimum value
    1
    2
    ages = [93, 99, 66, 17, 85, 1, 35, 82, 2, 77]
    youngest = min(ages)
  • Maximum value
    1
    2
    ages = [93, 99, 66, 17, 85, 1, 35, 82, 2, 77]
    oldest = maz(ages)
  • Arithmetic
    1
    2
    ages = [93, 99, 66, 17, 85, 1, 35, 82, 2, 77]
    total_years = sum(ages)

Slicing

  • Slice segments
    1
    2
    3
    4
    finishers = ["Ada", "Bob", "Charles", "Daniel", "Ernest"]
    first_two = finishers[:2]
    middle_three = finishers[1:4]
    last_three = finishers[-1:]

Rough idea

  • Keep and eyer on layers: (FROM, RUN, COPY).
  • You may check the volume on each step with docker history.

Docker commands

Review temporary files

  • Wrong: 248Mb: use a different layer for each instruction.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    FROM ubuntu
    RUN apt-get update
    RUN apt-get install -y wget xz-utils
    # clean up apt cache
    RUN rm -rf /var/lib/apt/lists/
    RUN wget https://node.org/dist/v12.13.1-linux-x64.tar.xz -O nodejs.tar.xz
    RUN tar xf nodejs.tar.xz
    RUN mkdir /opt/node && cp -r node-v12.13.1-linux-x64/* /opt/node/
    # clean up compressed files
    RUN rm -rf nodejs.tar.zx node-v12.13.-linux-x64
  • Right: 62.7Mb: reduce toa single RUN layer.

    1
    2
    3
    4
    5
    6
    7
    8
    FROM ubuntu
    RUN apt-get update \
    && apt-get install -y wget xz-utils \
    && rm -rf /var/lib/apt/lists/ \
    && wget https://node.org/dist/v12.13.1-linux-x64.tar.xz -0 nodejs.tar.xz \
    && tar xf nodejs.tar.xz \
    && mkdir /opt/node && cp -r node-v12.13.1-linux-x64/* /opt/node/ \
    && rm -rf nodejs.tar.zx node-v12.13.-linux-x64

Avoid using COPY

  • COPY creates a separate layer.
  • You may use wget on the RUN layer.

Use multistage builds

You may create a temporary image and then use it’s filesystem during the build of final image.
It installs all the dependencies and compile all the sources in intermediate image and copy.

  • Wrong: 260.5Mb - many built-in dependencies

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    FROM debian:9.6-slim as compiler

    #install apache thrift dependencies
    RUN apt-get update && apt-get install libstdc++ autoconf gcc
    tk-dev pkg-config libxft-dev build-essential wget curl make -y

    # download and install apache thrift
    RUN curl http://archive.apache.org/dist/thrift/0.11.0/thrift-0,11,0.tar.gz
    | tar zx
    RUN cd thrift-0.1..0/ \
    && ./configure --without-cpp \
    && make \
    && make install \
    && cd.. \
    && rm -rf thrift-0.11.0
  • Right: 55.5Mb

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    ## 1st image: original
    FROM debian:9.6-slim as compiler

    #install apache thrift dependencies
    RUN apt-get update && apt-get install libstdc++ autoconf gcc \
    tk-dev pkg-config libxft-dev build-essential wget curl make -y

    # download and install apache thrift
    RUN curl http://archive.apache.org/dist/thrift/0.11.0/thrift-0,11,0.tar.gz \
    | tar zx
    RUN cd thrift-0.1..0/ \
    && ./configure --without-cpp \
    && make \
    && make install \
    && cd.. \
    && rm -rf thrift-0.11.0

    ## 2nd image on multistage
    FROM debian:9.6-slim

    # copy thrift binary
    COPY --from=compiler /usr/local/bin/thrift /usr/local/bin/thrift

Java case

Use jlink to create an JRE with only the modules you need. Here is a jlink tutorial

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM adoptopenjdk/openjdk11:x86_64-ubuntu-jdk-11.28 as compiler
# create minimal jre
RUN jlink --module-path /opt/java/openjdk/jmods --verbose \
--add-modules java.base,java.logging,java.xml,java.scripting,jdk.jdwp.agent \
--compress 2 \
--no-heaer-files \
--output /opt/jre-11-minimal

# reduces the previous one to almost half of its size
FROM baseline
RUN mkdir -p /usr/lib/jvm
COPY --from=compiler /opt/jre-11minimal /opt/jre
RUN ln -s /opt/jre/bin/java /usr/bin/java

Reduce Java dependencies

Many images have the same dependencies, so they may be moved to another layer.

  • Using jlib, similar strategy to jlink.

    • One line method

      1
      2
      3
      4
      5
      # containerize the application
      mvn compile com.google.cloud.tools:jib-maven-plugin:2.3.0:build \
      -Dimage=<MY IMAGE>
      # build docker daemon
      mvn compile com.google.cloud.tools:jib-maven-plugin:2.3.0:dockerBuild
    • Full configuration method

      1. Setup maven project on pom.xml

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        <project>
        <build>
        <plugins>
        <plugin>
        <groupId>com.google.cloud.tools</groupId>
        <artifactId>jib-maven-plugin</artifactId>
        <version>2.3.0</version>
        <configuration>
        <to>
        <image>myimage</image>
        </to>
        </configuration>
        </plugin>
        </plugins>
        </build>
        </project>
      2. Change the image value for a valid registry

        1
        2
        3
        4
        5
        6
        <!-- Google Container Registry -->
        <image>gcr.io/my-gcp-project/my-app</image>
        <!-- Amazon Registry Container (ECR) -->
        <image>aws_account_id.dkr.ecr.region.amazonaws.com/my-app</image>
        <!-- Docker hub registry -->
        <image>docker.io/my-docker-id/my-app</image>
      3. Build image

        1
        2
        3
        4
        5
        6
        # build container image
        mvn compile jib:build
        # build to a docker daemon
        mvn compile jib:dockerBuild
        # buil and image tarball
        mvn compile jib:buildTar
  • Manual method: extract dependencies from one project and if they were upated, pushed them to your registry.

    1. On Maven with springboot: add excludeGroupIds parameter to your builder plugin configuration.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      <properties>
      <libraries.excludeGroupIds></libraries.excludeGroupIds>
      </properties>
      <!-- more stuff here -->
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
      <version>${springboot.version}</version>
      <configuration>
      <finalName>application</finalName>
      <excludeGroupIds>${libraries.excludeGroupids}</excludeGroupIds>
      <classifier>run</classifier>
      <layout>ZIP</layout>
      </configuration>
    2. use bash script.

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # get all dependencies
      EXCLUSIONS=`mvn -o dependecy:list | \
      grep ":.*:.*:.*"` | \
      cut -d] -f2- | \
      sed 's/:.*$//g' | \
      sort -u | \
      paste -d, -s | \
      tr -d "[:blank:]"`
      # build jarfile
      mvn install -Dlibraries.excludeGroupIds=${EXCLUSIONS}
      mvn dependency:copy-dependencies -DincludeScope=runtime \
      -Dmdep.prependGroupId=true -DoutputDirectory='./lib'
    3. Check if it is updated (you may do it checking he md5-hash of pom.xml).

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      DEPENDENCY_HASHES = $(echo `md5sum pom.xml module1/pom.xml \
      module2/pom.xml module3/pom.xml` \
      | md5sum | cut -d ' ' -f 1)

      dep_tag="${DOCKER_REGISTRY}/java-dependencies:${DEPENDENCIES_HASHIES}"
      docker pull ${dep_tag}

      if [ $? -ne 0]; then
      #image does not exist
      cp -r ./lib ./dependencies/
      # this directory contains dependencies.dockerfile
      cd ./dependencies/
      docker build --build-arg -t ${dep-tag}$ -f dependencies.dockerfile .
      docker push ${dep_tag}
      fi
    4. Generate dependencies.dockerfile

      1
      2
      3
      4
      5
      FROM custom-jre-build:jre11

      RUN mkdir /opt/applicaton/ &&
      mkdir /opt/application/lib/
      COPY ./lib /opt/application/lib/
    5. Pass dependencies hash to application’s dockerfile via ARG command and use it in version of FROM-image

      1
      2
      docker build --build-arg version=${DEPENDENCIES_HASHES} \
      -f application.dockerfile .
      1
      2
      3
      4
      5
      6
      7
      8
      9
      # aplication dockerfile
      ARG version
      FROM java-dependencies:$version

      COPY application.jar /opt/application/application.jar
      COPY run_java.sh /opt/erudite/run_java.sh
      RUN chmod +x /opt/erudite/run_java.sh

      CMD ["java", "-jar", "/opt/application/application.jar"]

Python case

Reduce dependencies

  1. Check the packages on dockerfile.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    ENV LANG=C.UTF-8
    ENV CONDA_DIR="/opt/conda"
    ENV PATH="$CONDA_DIR/bin:$PATH"

    RUN apt-get update $$ apt-get install libstdc++ autoconf openssl \
    libxft-dev build-essential make libevent-dev automake flex bison \
    libss11.0 \
    gcc libssl1.0 tk-dev build-essential ca-certificates openssl \
    wget curl libevent-dev automake libtool libbas3 liblacpack3 \
    liblapack-dev libblas-dev \
    libblas3 liblapack3 liblapack-dev libblas-dev bison pkg-config \
    g++ gfortran libpng-dev -y

    RUN pip install --upgrade pip && pip install --upgrade setuptools

    COPY requirements.txt requirements.txt

    # --no-cache-dir is important!
    RUN export LC_ALL=C && pip install --no-cache-dir -r requirements.txt

    FROM baseline

    RUN apt-get unstall g++ -y

    # copy python and installed dependencies
    COPY --from=compiler /opt/miniconda3/ /opt/miniconda3/

    RUN ln -s /opt3/miniconda3/bin/python /usr/bin/python \
    && ln -s /opt/miniconda3/bin/pip /usr/bin/pip

    CMD ["/bin/bash"]
  2. Create the final python image base on dependencies image.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ARG version
    FROM python-dependencies:$version

    RUN mkdir /opt/application
    COPY scripts/ /opt/application/scripts
    COPY run_python.sh /opt/application/run_python.sh
    RUN chmod +x /opt/application/run_python.sh

    CMD ["/bin/bash"]

Basics

Variables

  • assign

    1
    2
    message = "Hello world"
    print(message)
  • concatenantion

    1
    2
    3
    full_name ="First name: {}\nLastname: {} ".format(
    first_name,
    last_name)

User input

  • Strings

    1
    2
    3
    # default is string
    name = input("What's your name? ")
    print("Hello, " + name + "!")
  • Numbers

    1
    2
    3
    4
    age = input("How old are you? ")
    age = int(age)
    pi = input("What's the value of pi? ")
    pi = float(pi)

Lists

  • definition

    1
    2
    3
    4
    # assign
    bikes = ["mountain bike", "aegean blue"]
    # clone a list
    copy_of_bikes = bikes[:]
  • get item

    1
    2
    3
    4
    # get first
    first_bike = bikes[0]
    # get last
    last_bike = bikes[-1]
  • add and remove items

    1
    2
    3
    4
    bikes = []
    bikes.append("mountain bike")
    bikes.append("aegean blue")
    bikes.remove("aegean blue")
  • List comprehensions

    1
    double_values = [x*2 for x in range(1, 11)]
  • Slicing a list

    1
    2
    finishers = ["Ada", "Bob", "Charles"]
    first_two = finishers[:2]

Tuples

  • Non-editable lists
    1
    definition = (1366, 768)

Dictionaries

  • Key-value pairs items

    1
    2
    3
    4
    5
    6
    alien = {
    'color': 'green',
    'points': 2
    }

    print("The alien's color is " + alien['color'])
  • Adding a new key-value pair

    1
    alien['x_position'] = 0
  • Looping on dictionaries

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # loop on pairs
    fav_numbers = {'eric': 17, 'ever': 4}
    for name, number in fav_numbers.items():
    print(name + ' loves ' + str(number))

    # loop on keys
    fav_numbers = {'eric': 17, 'ever': 4}
    for name in fav_numbers.keys():
    print(name + ' loves a number')

    # loop on values
    fav_numbers = {'eric': 17, 'ever': 4}
    for number in fav_numbers.values():
    print(str(number) + ' is a favorite')

Control structures

Conditionals

  • Conditional tests

    Condition Representation
    equals x == 42
    not equals x != 42
    greater than x > 42
    greater or equal to x >= 42
    less than x< 42
    less or equal to x <= 42
  • Conditional test with lists

    1
    2
    'mountain bike' in bikes
    'car' not in bikes
  • Conditional blocks

    1
    2
    3
    4
    5
    6
    if age < 4:
    ticket_price = 0
    elif age < 18:
    ticket_price = 10
    else:
    ticket_price = 15

Loops

  • Conditional block
    1
    2
    3
    4
    current_value = 1
    while current_value <= 5:
    print(current_value)
    current_value += 1

Files

  • Read

    1
    2
    3
    4
    5
    file_name = 'demofile.txt'
    with open(file_name) as file_object:
    lines = file_object.readlines()
    for line in lines:
    print(line)
  • Write

    1
    2
    3
    4
    5
    6
    7
    8
    # overwrite
    file_name = 'journal.txt'
    with open(file_name, 'w') as file_object:
    file_object.write("I am coding Python.")
    # append
    filename = 'journal.txt'
    with open(filename, 'a') as file_object:
    file_object.write("\nThis is some appended text.")

Functions

  • Simple

    1
    2
    3
    4
    5
    6
    7
    # defintion
    def greet_user():
    # Display a simple greeting
    print("Hello!")

    # call
    greet_user()
  • With default value parameters

    1
    2
    3
    4
    5
    6
    def make_pizza(topping='bacon'):
    """Make a single-topping pizza."""
    print("Have a " + topping + " pizza!")

    make_pizza()
    make_pizza('pepperoni')
  • Returning a value

    1
    2
    3
    4
    def add_numbers(x, y):
    """Add two numbers and return the sum.""" return x + y

    sum = add_numbers(3, 5) print(sum)

Classes

  • Simple class

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Dog():
    """Represent a dog."""
    def __init__(self, name):
    """Initialize dog object."""
    self.name = name
    def sit(self):
    """Simulate sitting."""
    print(self.name + " is sitting.")

    my_dog = Dog('Buddy')

    print(my_dog.name + " is a great dog!")
    my_dog.sit()
  • Inheritance

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class SARDog(Dog):
    """Represent a search dog."""

    def __init__(self, name):
    """Initialize the sardog."""
    super().__init__(name)

    def search(self):
    """Simulate searching."""
    print(self.name + " is searching.")

    my_dog = SARDog('WToby')
    print(my_dog.name + " is a search dog.")
    my_dog.sit()
    my_dog.search()

Exceptions

  • Try/catch/else:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    prompt = "How many tickets do you need? "
    num_tickets = input(prompt)

    try:
    num_tickets = int(num_tickets)
    except ValueError:
    print("Please try again.")
    else:
    print("Your tickets are printing.")

Goal

How can you configure the coverture and show it on a project badge.

Java

Configure JaCoCo on the maven file

  • Configuration on pom.xml file

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    <dependencies>
    <dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
    </dependency>
    <dependency>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.5</version>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.5</version>
    <executions>
    <execution>
    <goals>
    <goal>prepare-agent</goal>
    </goals>
    </execution>
    <execution>
    <id>report</id>
    <phase>prepare-package</phase>
    <goals>
    <goal>report</goal>
    </goals>
    </execution>
    <execution>
    <id>jacoco-check</id>
    <goals>
    <goal>check</goal>
    </goals>
    <configuration>
    <rules>
    <rule>
    <limits>
    <limit>
    <counter>LINE</counter>
    <value>COVEREDRATIO</value>
    <minimum>90%</minimum>
    </limit>
    <limit>
    <counter>CLASS</counter>
    <value>MISSEDCOUNT</value>
    <maximum>0</maximum>
    </limit>
    </limits>
    </rule>
    </rules>
    </configuration>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>
    <reporting>
    <plugins>
    <plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <reportSets>
    <reportSet>
    <reports>
    <report>report</report>
    </reports>
    </reportSet>
    </reportSets>
    </plugin>
    </plugins>
    </reporting>
  • Generate reports (on your CLI, or Run Maven Configuration on your IDE)

    1
    mvn clean install test jacoco:report

Configure Gitlab

  • CI file on project (gitlab-ci.yml)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    image: maven:3.3.9-jdk-8
    cache:
    paths:
    - /root/.m2/repository/

    test:
    script:
    - mvn clean install
    - mvn test jacoco:report
    - cat target/site/jacoco/index.html |
    grep -o 'Total[^%]*%' |
    grep -o '<td class="ctr2">[^%]*%' |
    sed -e 's/<td class="ctr2">/Jacoco-Test-Coverage:/g'
    coverage: '/Jacoco-Test-Coverage:(\d+\.?\d+)%/'
    artifacts:
    reports:
    junit:
    - target/surefire-reports/TEST-*.xml
    - target/failsafe-reports/TEST-*.xml

Python

Install and launch coverage

  • Install coverage (on your Python CLI, use pip)

    1
    python -m pip install coverage
  • Generate reports

    1
    2
    3
    4
    5
    6
    # run
    python -m coverage run -m --source="directory_to_inspect" unittest discover
    # get coverage report on CLI
    python -m coverage report
    # get html coverage
    python -m coverage html

Configure Gitlab

  • CI file on project (gitlab-ci.yml)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    image: python:latest

    before_script:
    - python -V # print out python version for debugging
    - python -m pip install --upgrade pip
    - python -m pip install coverage

    stages:
    - test

    test_job:
    stage: test
    script:
    - echo "Running tests"
    - python -m unittest discover -s "./tests/"
    - python -m coverage run -m \
    --source="directory_to_inspect" \
    unittest discover
    - python -m coverage report
    coverage: '/^TOTAL.+?(\d+\%)$/'

NodeJS

Install and launch coverage

  • Install coverage (on your npm CLI, use npm)

    1
    npm install nyc
  • Generate reports

    1
    2
    3
    4
    5
    # you may store this a script on package.json
    "scripts": {
    "test": "node jasmine.js",
    "coverage": "nyc --reporter=lcov --reporter=text-summary npm run test"
    },

Configure Gitlab

  • CI file on project (gitlab-ci.yml)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    image: node:12-alpine # use nodejs v12 LTS
    cache:
    paths:
    - node_modules/

    before_script:
    - npm install

    stages:
    - test

    test_job:
    stage: test
    script:
    - npm install jasmine --save
    - npm install jasmine-console-reporter --save-dev
    - npm install nyc
    - npm test
    - npm run coverage
    coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
    artifacts:
    paths:
    - public
    only:
    - master
0%