Gitlab CI don't test if there are no changes to the code (pytest-testmon) - gitlab

I want to perform tests using gitlab CI using pytest-testmon. Unfortunately it seems that the generated file .testmondata is not saved in the gitlab-runner instances, so that no recollection of what was tested is saved.
Is there a way of saving this .testmondata and using it (for every different branch at test) in order to avoid repeating tests of unchanged code when code is pushed to those branches?

You can upload files generated during your job to Gitlab so you can view them later by downloading them, viewing on a Merge Request, or using them in other jobs by using the artifacts keyword. Here's a simple example:
tests:
stage: tests
script:
- ./run_tests.sh
artifacts:
paths:
- .testmondata
name: "$CI_COMMIT_REF_NAME_tests_testmondata"
expose_as: 'Tests Results'
expire_in: 3 months
In this example, after your script section (and before_script or after_script if present) runs, gitlab-runner will look for a file/directory that matches the entries in the paths array. If none are found, it will throw an error.
The runner will then upload the artifacts to Gitlab using the name in the name field. It's good practice to use some of the predefined variables that Gitlab CI provides so you can easily identify which pipeline and job an artifact came from, if necessary. In this example the name has the branch or tag name, and then _tests_testmondata.
The next attribute, expose_as lets you put a link on a Merge Request for this branch to display the artifacts. Without expose_as, you'll have to open the Pipeline and Job to view the artifacts.
Next, expire_in lets you define when the artifacts from this job should expire. If you don't set this attribute, the artifacts will expire based on the Gitlab server's setting, which by default is 30 days. You can supply a number of seconds as an int (3600, or one hour), never (to never expire), or many other human-readable formats like 3 years 8 months 28 days or 46 months.
You can view all the available predefined variables here: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html, and you can view all the options for the artifacts keyword here: https://docs.gitlab.com/ee/ci/yaml/#artifacts

Related

How to read labels in Gitlab CI script

I have a few use cases in my Gitlab setup I would like to be able to support:
If a certain label (let's call it “skip_build”) is set, the deployment steps should not be run when I merge an MR to a main branch. This would be useful when we have multiple MRs being merged right after another and only need the last one built.
If another label (we'll call it “skip_tests”) is set, I should be able to read it as an env var from within the script and alter the flow within the script accordingly (using normal bash syntax), e.g. to alter the package command parameters used a bit. This is useful for small changes where it might not make sense to run a lengthy test suite.
Is this possible with Gitlab, and if so, how?
I’ve tried experimenting with CI_MERGE_REQUEST_LABELS, but it doesn’t seem to be able to read that as an env var from within the script.
You have to use merge request pipelines for the CI_MERGE_REQUEST_LABELS variable (and other MR-related variables) to be present as documented in predefined variables.
You could use a rules: clause to skip jobs. Something like
build:
rules: # only run this job if the regex pattern does not match
- if: $CI_MERGE_REQUEST_LABELS !~ /skip_build/
You can also do this on any other kind of predefined (or user-defined) variable, like branch name, commit messages, MR titles, etc. Whatever works for you.
For example, a built in feature of GitLab is that if your commit message contains [ci skip] it will prevent the pipeline from running. You could implement similar functionality for your jobs and/or pipelines through rules: or workflow:rules:.

Override a defined CI/CD variable in a gitlab job

i am currently facing an issue using Gitlab-ci. i am using gitlab 13.12 community edition.
I want to be able to override the value of a CICD variable from within a gitlab-ci.yml job.
you might say that i could just pass out the value from one job to another but the goal is to change that value so that , on the next pipeline , all my jobs will use the updated one.
To be precise , i want to be able to change MY_CICD_VARIABLE_TO_CHANGE that i've defined in project > cicd > variables. and update this value from a gitlab-ci job
something like this but not only change it for the current pipeline but update it :
change_variable_value:
stage: "change_variable"
image: myimage
script:
- $MY_CICD_VARIABLE_TO_CHANGE="value_changed"
, i tried to do every single solutions specify here :
https://docs.gitlab.com/13.12/ee/ci/variables/index.html#override-a-defined-cicd-variable
nothing seems to work.
I also tried to use artefact but it seems that i am not able to pass artefact between 2 distinct pipelines, they have to be part of the same pipeline , even by curling the api (at least in the community edition)
Any idea is more than welcome :)

How to access previous GitLab CI artifacts, process them and save file in "main" repository?

My application for this is to visualize the performance of my software. Therefore I briefly describe what I'm doing and where I'm stuck.
I have my source code in GitLab
Compile and run some tests in the CI for each commit
Measure the time it took for the test run and save it to a file
Upload the file with the time as an artifact
--------- From here on I don't know how to achieve it.
Run some new job that reads all timing files of the previous artifacts
Plot the times, probably with Python and save the image as SVG in the "main" repository
Show the image on the GitLab start page (README.md should probably include it)
Now I see which commits had which impact on my software's performance
No idea whether I'm asking for the impossible or not. I hope someone can help me as I'm not an CI expert. Maybe a single expression is already enough to google the solution but I don't even know how to formulate this.
Thanks everyone :)
Committing Images to Main
You can't just save an SVG image to the main repo from a pipeline job. You would need to make a commit. Not only would that pollute your git history and bulk up your repo, but it could also trigger a new pipeline, resulting in an endless loop.
There are ways around the endless loop, e.g. by controlling which sources/branches trigger pipelines or by prefixing the commit message with [skip ci], but it can get complicated and is probably not worth it. The truth is GitLab cannot do exactly what you want, so you will have to compromise somewhere.
Generate Metrics Graphs From Artifacts
You can collect metrics from past pipelines in a CSV file and save it as an artifact.
Add this to a reusable script called add_metrics.sh:
#!/bin/bash
HTTP_HEADER="PRIVATE-TOKEN: $YOUR_ACCESS_TOKEN"
URL_START="https://gitlab.example.com/api/v4/projects/$CI_PROJECT_ID/jobs/artifacts"
URL_END="raw/<path/to/artifact>/metrics.csv?job=$CI_JOB_NAME"
COLUMN_HEADERS=Date,Time,Branch,Commit SHA,Test Time(s),Code Coverage (%)
# download latest artifact
if curl --location --header $HTTP_HEADER $URL_START/$CI_COMMIT_BRANCH/$URL_END
then echo "Feature branch artifact downloaded."
elif curl --location --header $HTTP_HEADER $URL_START/master/$URL_END
then echo "Master branch artifact downloaded."
else echo $COLUMN_HEADERS >> metrics.csv
fi
# add data sample row to CSV
NOW_DATE=$(date +"%F")
NOW_TIME=$(date +"%T")
echo $NOW_DATE,$NOW_TIME,$CI_COMMIT_BRANCH,$CI_COMMIT_SHA,$TEST_TIME,$CODE_COVERAGE >> metrics.csv
# keep last 50 lines
echo "$(tail -50 metrics.csv)" > metrics.csv
Then call it from your pipeline in gitlab-ci.yml:
job_name:
script:
- TEST_TIME=10
- CODE_COVERAGE=85
- chmod +x add_metrics.sh
- bash add_metrics.sh
artifacts:
paths:
- metrics.csv
expire_in: 1 month
Note: You will have to create a personal token and add it to a masked variable. I will also leave it up to you to populate the data metrics, like test time, code coverage, etc.
Explanation of Code
Download the latest artifact for the current branch.
The first commit of a feature branch will not find a "latest" artifact. If that happens, download the latest artifact from master.
The first time the script runs, master won't even have a "latest" artifact, so create a new CSV file.
APPEND the current sample to the end of the CSV file. Delete old samples to keep a fixed number of data points. You can add date, pipeline ID and other metrics.
Store the updated artifact.
To view the graph, download the artifact from the GitLab UI and view it in a spreadsheet app.
Publish to Pages
Using Python (pandas, matplotlib), you can generate an image of the plot and publish it to Gitlab Pages from your master branch pipeline. You can have a static HTML page in your repository referencing the same image filename, and keep replacing the same image from your pipeline. You can also add more useful metrics, such as code coverage.

How can I prevent jobs from running based on the files changed compared with master?

I have some gitlab jobs in my pipleline which are slow and I'd like to prevent them from running when the changes will not affect the job's outcome.
This is what I have tried:
run_tests:
stage: checks
script:
- cargo test
except:
- master
- tags
only:
changes:
- "**/*.rs"
- "**/Cargo.toml"
- "**/Cargo.lock"
This sort of works. If a merge request has multiple commits, this job will not run on any of the commits after the first one, unless a Rust source file has changed.
But the job will still always run on the first commit of the branch, even if there are no changes to Rust source files between this branch and master. Even worse, if the tests fail on the first commit, subsequent commits might skip the tests so broken code could get merged.
How can I change this filter so that the diff is done against the target branch of the merge request?
Hidden quite deep in the documentation for only: changes, there is this snippet:
Without pipelines for merge requests, pipelines run on branches or tags that don’t have an explicit association with a merge request. In
this case, a previous SHA is used to calculate the diff, which
equivalent to git diff HEAD~. This could result in some unexpected
behavior, including:
When pushing a new branch or a new tag to GitLab, the policy always evaluates to true.
When pushing a new commit, the changed files are calculated using the previous commit as the base SHA
Which is the scenario you are currently experiencing, where the job always runs on the first commit of the new branch.
For this to work as you want, I believe you need to add only: merge_requests, however this would require changing your entire pipeline workflow to integrate only: merge_requests to your pipeline.
From the documentation:
With pipelines for merge requests, it’s possible to define a job to be
created based on files modified in a merge request.
In order to deduce the correct base SHA of the source branch, we
recommend combining this keyword with only: [merge_requests]. This
way, file differences are correctly calculated from any further
commits, thus all changes in the merge requests are properly tested in
pipelines.
Various issues from GitLab:
https://gitlab.com/gitlab-org/gitlab/-/issues/11427
https://gitlab.com/gitlab-org/gitlab/-/issues/27875

How do you reuse a before_script from a shared yml file in Gitlab CI?

I know that you can reuse blocks of code in a before script using yaml anchors:
.something_before: &something_before
- echo 'something before'
before_script:
- *something_before
- echo "Another script step"
but this doesn't seem to work when the .something_before is declared in a shared .yml file via the include:file. It also does not seem that extends works for before_script. Does anyone know a way of reusing some steps in a before_script from a shared .yml file?
EDIT: My use case is that I have 2 gitlab projects with almost identical before_script steps. I don't want to have to change both projects whenever there's a change, so I have a third, separate Gitlab project that has a .yml template that I am including via include:file in both projects. I want to put all the common code in that shared template, and just have like two lines before_script for the git project that has the two extra steps.
Yaml anchors don't work with included files. You need to use the extends keyword. But what you want to achieve won't work with before_script as code in your template will be overwritten in the job which uses the template if there is a before_script as well.
Do you really need a before_script in your specific job or can you achieve the same with a normal script? If yes you can do something like this:
Template File:
.something_before:
before_script:
- echo 'something before'
- echo 'something more before'
Project Pipeline:
include:
- project: 'my-group/my-project'
file: '/something_before.yml'
stages:
- something
something:
stage: something
extends: .something_before
script:
- echo 'additional stuff to do'
And your before_script section will be merged into the something job and executed before the script part.
See if GitLab 13.6 (November 2020) does make it easier:
Include multiple CI/CD configuration files as a list
Previously, when adding multiple files to your CI/CD configuration using the include:file syntax, you had to specify the project and ref for each file. In this release, you now have the ability to specify the project, ref, and provide a list of files all at once. This prevents you from having to repeat yourself and makes your pipeline configuration less verbose.
See Documentation) and Issue.
And even, with GitLab 14.9 (March 2022):
Include the same CI/CD template multiple times
Previously, trying to have standard CI/CD templates that you reuse in many places was complicated because each template could only be included in a pipeline once.
We dropped this limitation in this release, so you can include the same configuration file as many times as you like.
This makes your CI/CD configuration more flexible as you can define identical includes in multiple nested configurations, and rest assured that there will be no conflicts or duplication.
See Documentation and Issue.
You can use extends without any problem, but you will need to overwrite the entire before_script block.
If you want to change just a piece of your before_script, use a shell script to do it
Set the if condition inside of your template
before_script
- |
if [ condition ]
then
commands here
fi
AFTER EDIT: You can use variables to achieve it
Project 1: VAR = command 1
Project 2: VAR = command 2
You can set the content of env var on the gitlab-ci.yml file or on the CI/CD settings in each project!

Resources