MOTOSHARE 🚗🏍️
Turning Idle Vehicles into Shared Rides & Earnings
From Idle to Income. From Parked to Purpose.
Earn by Sharing, Ride by Renting.
Where Owners Earn, Riders Move.
Owners Earn. Riders Move. Motoshare Connects.
With Motoshare, every parked vehicle finds a purpose.
Owners earn. Renters ride.
🚀 Everyone wins.
The extends keyword in GitLab CI/CD allows you to reuse configuration from other job definitions or “hidden” job templates within your .gitlab-ci.yml file. This is a powerful feature for practicing DRY (Don’t Repeat Yourself) principles, making your CI/CD configuration more maintainable, consistent, and easier to manage.
“Hidden” jobs are typically used as templates. Their names start with a dot (.), and they are not processed as regular jobs by GitLab CI unless explicitly extended.
Example .gitlab-ci.yml with extends:
# .gitlab-ci.yml
stages:
- setup
- test
- build
default:
image: alpine:latest # A global default image
# 1. Define a hidden job template for common test configurations
.base_test_config:
stage: test
image: ruby:3.0 # Specific image for test jobs
tags:
- docker # Default tags for these test jobs
variables:
TEST_SUITE_VERSION: "v1.2"
RAILS_ENV: "test"
before_script:
- echo "--- Base Test Config: before_script ---"
- echo "Setting up common test environment..."
- bundle install --jobs $(nproc) --path vendor/bundle # Example common setup
script: # This is a base script; jobs extending this can add more commands
- echo "--- Base Test Config: script ---"
- echo "Running common pre-test checks..."
after_script:
- echo "--- Base Test Config: after_script ---"
- echo "Cleaning up common test resources."
# 2. Define another hidden job template, perhaps for a specific type of test
.rspec_test_template:
extends: .base_test_config # This template itself extends another one
variables:
TEST_FRAMEWORK: "RSpec" # Overrides or adds to variables from .base_test_config
before_script: # Commands here will run AFTER commands from .base_test_config's before_script
- echo "--- RSpec Template: before_script ---"
- echo "Setting up RSpec specific environment..."
script: # Commands here will run AFTER commands from .base_test_config's script
- echo "--- RSpec Template: script ---"
- echo "Executing RSpec tests with framework $TEST_FRAMEWORK on Rails env $RAILS_ENV"
# Placeholder for actual rspec command
# --- Regular Jobs using 'extends' ---
# Job 1: Extends the base test configuration
unit_tests:
extends: .base_test_config
# This job inherits: stage, image, tags, variables (TEST_SUITE_VERSION, RAILS_ENV),
# before_script, script, and after_script from .base_test_config.
script: # Commands here run AFTER the script block from .base_test_config
- echo "--- Unit Tests: script ---"
- echo "Running unit tests for version $TEST_SUITE_VERSION..."
- echo "Actual unit test commands would go here."
# Job 2: Extends the RSpec template (which itself extends .base_test_config)
# It also overrides a variable and adds specific script commands.
feature_tests_rspec:
extends: .rspec_test_template
variables:
RAILS_ENV: "test_feature" # Overrides RAILS_ENV from .base_test_config (via .rspec_test_template)
SPEC_FILES: "spec/features"
script: # Commands here run AFTER script blocks from .base_test_config AND .rspec_test_template
- echo "--- Feature Tests (RSpec): script ---"
- echo "Running RSpec feature tests from $SPEC_FILES on Rails env $RAILS_ENV"
- echo "Test Suite version: $TEST_SUITE_VERSION, Framework: $TEST_FRAMEWORK"
- bundle exec rspec $SPEC_FILES # Example actual command
# Job 3: Extends the base but completely overrides its 'script' and 'before_script'
# while keeping other things like image and variables.
custom_setup_test:
extends: .base_test_config
before_script: # This completely replaces .base_test_config's before_script
- echo "--- Custom Setup Test: entirely new before_script ---"
- echo "Performing a very different setup."
script: # This completely replaces .base_test_config's script
- echo "--- Custom Setup Test: entirely new script ---"
- echo "Running tests with custom setup. RAILS_ENV is $RAILS_ENV."
# Job 4: Example of a non-test job, not using extends
# This job will use the global default image 'alpine:latest'
build_documentation:
stage: build
script:
- echo "Building documentation..."
- mkdir public
- echo "Docs built" > public/index.html
artifacts:
paths:
- publicCode language: PHP (php)
Explanation:
- Hidden Job Templates (
.prefix):- Jobs like
.base_test_configand.rspec_test_templatestart with a dot (.). This makes them “hidden” jobs. - Hidden jobs are not run directly as part of your pipeline. They serve as templates that other regular jobs can inherit from using the
extendskeyword.
- Jobs like
extends:Keyword:- Used within a job definition to specify one or more template jobs whose configurations should be inherited.
extends: .base_test_configmeans theunit_testsjob will inherit all configurations from.base_test_config.extends: .rspec_test_templatemeansfeature_tests_rspecinherits from.rspec_test_template. Since.rspec_test_templateitself extends.base_test_config,feature_tests_rspeceffectively inherits from both, with.rspec_test_template‘s definitions taking precedence over.base_test_configwhere there are overlaps, and thenfeature_tests_rspec‘s own definitions taking precedence over both.
- Merge Strategy (How Inheritance Works):
GitLab performs a “deep merge” of the configurations. The specific job’s configurations always take precedence over what’s inherited.- Scalars (single values like
image,stage): The value from the extending job (the most specific one) is used. If the extending job doesn’t define it, the value from the template is used. - Hashes (key-value pairs like
variables): They are merged. If both the template and the extending job define the same variable key, the value from the extending job (the most specific one) wins. New variables from either are included.- Example:
.base_test_configdefinesRAILS_ENV: "test".feature_tests_rspecdefinesRAILS_ENV: "test_feature". So,feature_tests_rspecwill usetest_feature.
- Example:
- Arrays (lists like
script,before_script,after_script,tags):script: Commands from the extended template’sscriptare executed before the commands in the extending job’sscript. They are effectively prepended.before_script: Commands from the extended template’sbefore_scriptare executed before the commands in the extending job’sbefore_script. They are prepended.after_script: Commands from the extended template’safter_scriptare executed before the commands in the extending job’safter_script. They are prepended.tags: Thetagsarray from the extending job replaces thetagsarray from the template.- Important Exception: If an extending job redefines
script,before_script, orafter_scriptcompletely (as shown incustom_setup_test), the template’s corresponding script block is not prepended; the job’s definition entirely replaces it for that specific script block. To achieve prepending, the job must also include the script keywords from the template if it wants to re-define only parts, whichextendshandles implicitly for these script arrays if the job also has a script section. Correction based on typical CI behavior: Forscript,before_script, andafter_script, GitLab CI prepends the inherited script content to the specific job’s script content. If you want to completely override, you’d typically need to ensure the template doesn’t have that script section, or you are actually replacing a more deeply nested script. However, the common understanding and use ofextendswith these script arrays is that the inherited commands run, then the specific job’s commands run. My examplecustom_setup_testreflects this prepending forbefore_scriptandscript. - Self-correction for clarity: The most accurate way to describe
script,before_script, andafter_scriptmerging withextendsis: the commands from the extended configuration are executed, then the commands from the current job’s configuration for that same key are executed. They are effectively combined in sequence.
- Scalars (single values like
unit_testsJob:- Inherits
stage: test,image: ruby:3.0,tags,variables, thebefore_script, the initialscriptpart, andafter_scriptfrom.base_test_config. - Its own
scriptblock adds more commands that run after thescriptblock from.base_test_config.
- Inherits
feature_tests_rspecJob:- Inherits from
.rspec_test_template. .rspec_test_templatefirst inherits from.base_test_config.- Then,
.rspec_test_template‘s own definitions (likeTEST_FRAMEWORK: "RSpec"and itsbefore_scriptandscript) are merged. - Finally,
feature_tests_rspec‘s own definitions (likeRAILS_ENV: "test_feature"and itsscript) are merged, taking highest precedence. - The
before_scriptcommands will run in order:.base_test_config‘s ->.rspec_test_template‘s. - The
scriptcommands will run in order:.base_test_config‘s ->.rspec_test_template‘s ->feature_tests_rspec‘s.
- Inherits from
custom_setup_testJob:- This job extends
.base_test_configbut provides its own completebefore_scriptandscriptdefinitions. In this case, for these specific keys, the definitions from.base_test_configare prepended to the definitions incustom_setup_test. - Correction/Clarification: If a job defines
scriptand extends a template that also definesscript, the inherited script runs, then the job’s script runs. The example above where I said it “replaces” was slightly off for these script arrays; the behavior is more like chaining.
- This job extends
- Extending Multiple Templates:
- You can extend multiple templates:
extends: [.template_A, .template_B]. - Configurations are merged from left to right.
.template_B‘s values would override.template_A‘s if there are conflicts, and then the current job’s values override anything from the templates.
- You can extend multiple templates:
Benefits of extends:
- DRY (Don’t Repeat Yourself): Reduces a lot of boilerplate and repeated configuration.
- Consistency: Ensures common settings (like Docker images, setup scripts, tags) are applied consistently across similar jobs.
- Maintainability: If you need to change a common setting (e.g., update a Docker image version), you only need to change it in the template, and all jobs extending it will get the update.
- Readability: Job definitions become shorter and focus on what’s unique to them.
extends is a very powerful tool for organizing your GitLab CI/CD configurations, especially as your pipelines grow in complexity. It’s preferred over older YAML anchor/alias methods for CI configurations because extends performs a more intelligent “deep merge” suitable for CI job structures.