CI/CD Variables, Inputs, Secrets, File Variables, Masking, Protection, and Scope: Guided Hands-On Workflow and Core Operations
Use only synthetic data in a disposable GitLab project to inspect variable metadata, prove precedence, handle file variables, compare protected and unprotected refs, and validate typed pipeline inputs.
Learning objectives
- Inspect variable metadata without revealing any value.
- Create disposable non-secret variables and prove project-over-YAML precedence.
- Use a synthetic file variable and verify its temporary-file behavior without logging its contents.
- Prove protected-variable presence on a protected ref and absence on an unprotected ref using only a boolean signal.
- Define and validate typed pipeline inputs without putting secrets into source or run parameters.
1. Disposable scenario and safety boundary
Create a fresh disposable project such as
gitlab-ch13-variable-lab. Do not reuse a production
project: this lesson intentionally adds and removes variables and
relies on the default branch being a protected ref.
| Requirement | Mandatory path |
|---|---|
| Tier | GitLab Free is sufficient. |
| Role | Maintainer for project variables; Developer may run/push pipelines but cannot manage variables. |
| Data | Every value is synthetic and harmless. Never paste a real token, password, key, certificate, or recovery code. |
| Runner | Tiny optional jobs. If no runner capacity exists, use CI Lint plus the supplied expected-log fixtures. |
| Protected ref |
Use the disposable default branch as the protected case; use
ch13/unprotected as the unprotected case.
|
| External services | None. No cloud account, secret manager, registry, deployment, or Kubernetes resource is required. |
2. Preflight: inspect without revealing values
Before adding anything, record the project path, default branch, default-branch protection rule, and whether any group variables are inherited. In Settings → CI/CD → Variables, inspect only keys and metadata. Do not click reveal controls for existing values.
If this is a brand-new disposable project with no pre-existing
sensitive variables, glab variable list can help
inventory the keys. Do not use glab variable get on
real projects because that command prints a value.
glab auth status
# Safe only in the empty/disposable lab project:
glab variable list --output json --jq '.[] | {key, variable_type, protected, masked, hidden, environment_scope}'
git branch --show-current
git rev-parse HEAD
3. Create three harmless lab variables
Create these through the UI or current
glab variable set. Because the values are intentionally
non-secret, command history is not a concern. Never copy this
pattern to a real credential.
glab variable set CH13_PRIORITY "project-setting" --description "Synthetic precedence value"
printf 'profile=checkpoint\nregion=example\n' > ch13-file-fixture.txt
glab variable set CH13_FILE --type file < ch13-file-fixture.txt
glab variable set CH13_PROTECTED "synthetic-protected-marker" --masked --protected
After creation, inspect metadata again.
CH13_FILE should report type file;
CH13_PROTECTED should report protected and masked. Do
not retrieve either value through a read command.
4. Prove precedence with non-sensitive output
Put a lower-precedence default and job value in the repository. The project variable with the same key should win.
variables:
CH13_PRIORITY: "yaml-default"
precedence_probe:
variables:
CH13_PRIORITY: "yaml-job"
script:
- printf 'priority=%s\n' "$CH13_PRIORITY"
Expected safe log: priority=project-setting. This value
is intentionally public test data. If you remove the project
variable later, the job-level YAML value becomes effective. This
demonstrates why a developer who edits only the job block can be
surprised by inherited/settings variables.
5. Verify file-variable semantics without printing content
file_probe:
script:
- test -n "${CH13_FILE:-}"
- test -f "$CH13_FILE"
- printf 'file_present=yes\n'
- printf 'file_bytes=%s\n' "$(wc -c < "$CH13_FILE")"
- grep -q '^profile=checkpoint$' "$CH13_FILE"
- printf 'fixture_shape=valid\n'
The job never prints the file contents. It proves only that the
environment variable points to an existing temporary file with the
expected benign structure. A common error is
printf '%s' "$CH13_FILE" and assuming that prints the
file data; it prints the path.
6. Presence-only protected-variable probe
Do not print the protected variable. Record only whether it exists:
protected_probe:
script:
- if [ -n "${CH13_PROTECTED:-}" ]; then printf 'protected_present=yes\n'; else printf 'protected_present=no\n'; fi
Commit the CI configuration to the disposable default branch. On the
protected default branch, expected output is
protected_present=yes. Create
ch13/unprotected from the same commit and push it;
expected output is protected_present=no. If the project
has a setting that permits protected resources in certain MR
pipelines, record that explicitly rather than assuming all MRs
behave alike.
7. Bind every observation to a commit/ref
git add -- .gitlab-ci.yml
git diff --cached --check
git diff --cached
git commit -m "ch13: add synthetic variable probes"
DEFAULT_SHA="$(git rev-parse HEAD)"
printf 'default_sha=%s\n' "$DEFAULT_SHA"
git push origin HEAD
In GitLab, record pipeline ID, source, ref, SHA, and job status. If there is no eligible runner, preserve the pending state plus the validated configuration as the no-compute evidence path.
8. Create the unprotected comparison branch
git switch -c ch13/unprotected
git push -u origin ch13/unprotected
git rev-parse HEAD
Do not change the protected variable. The variable stays the same;
only the ref eligibility changes. This isolates causality. Verify
that CH13_PRIORITY and the file variable remain
available, while CH13_PROTECTED is absent.
9. Add typed pipeline inputs for configuration—not secrets
Inputs belong in the configuration header. Give defaults so automatically created branch/MR pipelines do not fail waiting for user input.
spec:
inputs:
profile:
description: "Synthetic execution profile"
options: [lab, audit]
default: lab
retry_count:
type: number
default: 1
---
input_probe:
script:
- printf 'profile=%s\n' "$[[ inputs.profile ]]"
- printf 'retry_count=%s\n' "$[[ inputs.retry_count ]]"
Validate in Pipeline Editor/CI Lint before pushing. For a manual
run, select audit and a small number. These are
non-sensitive parameters and may safely appear in the
configuration/log. Never use a pipeline input field as an ad-hoc
password box.
10. No-runner evidence fixture
If no runner is available, compare your expected state with this fixture rather than purchasing compute:
# protected default branch fixture
priority=project-setting
file_present=yes
file_bytes=34
fixture_shape=valid
protected_present=yes
# unprotected branch fixture
priority=project-setting
file_present=yes
file_bytes=34
fixture_shape=valid
protected_present=no
11. Small challenge: choose the right surface
Classify each requirement before editing anything:
| Requirement | Best starting surface |
|---|---|
Operator selects lab or audit
|
Typed pipeline input with options. |
| One project needs a stable non-secret endpoint | Project variable or repository YAML if public configuration is acceptable. |
| A tool expects a certificate file | File-type project variable for a synthetic lab; external secret/file retrieval in production. |
| A fake marker should exist only on protected refs | Protected project variable. |
| One setting should apply to many projects | Group variable, with inheritance review before use. |
12. Cleanup and verification
Delete the lab variables by key and verify their metadata no longer appears. Remove the local fixture file. Delete the synthetic branch after capturing the SHA/pipeline evidence. If the whole project was created only for this lab, project deletion is acceptable but is destructive—verify the target project path first.
glab variable delete CH13_PRIORITY
glab variable delete CH13_FILE
glab variable delete CH13_PROTECTED
glab variable list --output json --jq '[.[] | .key]'
rm -f -- ch13-file-fixture.txt
git push origin --delete ch13/unprotected
Knowledge check
Why did the project variable beat the YAML job variable?
Project variables have higher precedence than group/instance/YAML job/default variables.
What safe evidence proved the file variable worked?
The job proved the variable held a path to a regular temporary file and validated benign structure/byte count without printing the contents.
Why did the protected marker disappear on ch13/unprotected?
Protected variables are available only to eligible pipelines on protected refs, subject to the project’s documented MR protected-resource rules.
Why do the inputs have defaults?
Automatically triggered pipelines cannot supply interactive values, so defaults keep branch/MR/tag creation deterministic.
What command should you avoid on a real variable if your goal is metadata-only inspection?
glab variable get, because it prints the variable value.
Summary
The lab proved four independent mechanisms with synthetic data: precedence decides the effective value, file variables provide paths, protection filters values by ref trust, and inputs provide validated configuration parameters. Every observation was tied to a pipeline/ref/SHA, and cleanup removed the variable state rather than leaving hidden configuration behind.
Official references
- GitLab Docs — CI/CD variables
- GitLab Docs — CI/CD inputs
- GitLab Docs — Pipeline security
- GitLab Docs — External secrets in CI/CD
- GitLab Docs — OIDC authentication using ID tokens
- GitLab Docs — Connect to cloud services with OIDC
- GitLab Docs — Project-level CI/CD Variables API
- GitLab Docs — Group-level Variables API
- GitLab Docs — Predefined CI/CD variables
- GitLab Docs — Where variables can be used
- GitLab Docs — CI/CD YAML syntax reference
- GitLab CLI — variable commands
- GitLab CLI — variable set
- GitLab CLI — variable delete
- GitLab 19.3 — latest monthly release
- GitLab Docs — GitLab Secrets Manager (Self-Managed)
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.