| name | gitlab-project-bootstrap |
| description | Set up or audit GitLab project metadata hygiene (avatar, description, topics, badges, README) for Cinetic Digital's private client repos on gitlab.com. Use this whenever the user is starting a brand-new client project, asks to "configure" or "bootstrap" a GitLab project, wants project descriptions/tags/topics/badges set, mentions a project's README is missing/stock/hidden/needs improving, or asks what's missing / what should be set up on a GitLab repo. Also trigger when the user mentions a specific client project by name (e.g. a PrestaShop or Laravel repo) and asks about its GitLab page, its description, or wants it to "look proper." Works both for brand-new projects (apply everything from minute 1) and for auditing/fixing an existing one.
|
| version | 0.1.0 |
| metadata | {"author":"Eduardo Calvo"} |
GitLab project bootstrap (Cinetic Digital client repos)
Checklist + concrete commands for making a private client repo on gitlab.com (group cineticd) look and behave properly from day one — description, topics, badges, README, and a couple of org-hygiene checks worth flagging. Built from real work across PrestaShop projects (ps9-gaudibarcelonashop, vives-8, milagros-colombia-b2b-v9.1) and a Laravel one (apply-animalmax).
Everything here is done via glab (GitLab CLI) — either glab api for project-metadata-only changes (no repo files, no PR needed), or a normal clone → branch → MR → merge flow when a repo file (README) needs to change.
Before you start: find the project and its SSH remote alias
If you don't have the exact namespace/project path, search for it:
glab api "projects?membership=true&per_page=100&search=<name>" | python3 -c "
import json,sys
for p in json.load(sys.stdin):
print(p['path_with_namespace'], '|', p.get('description'))
"
If there are multiple near-matches (e.g. vives-8, vives-17-nuevo, ps17-vivescortadaimport), confirm the right one with the user rather than guessing — don't touch the wrong client's repo.
Check ~/.ssh/config for the SSH host alias used to reach gitlab.com — this org uses a Host gl-cinetic alias (not git@gitlab.com: directly) with its own IdentityFile. Cloning with the wrong host will fail with "Permission denied (publickey)" even though your GitLab account has access. Always clone as git@gl-cinetic:<path>.git, and run glab mr create/glab mr merge from inside the cloned repo (not with --repo <path> from elsewhere) — glab otherwise tries to add its own remote using the default host and fails the same way.
1. Avatar
GitLab defaults to a colored initial-letter avatar if none is set — looks unfinished on a client project page. This one's manual: Settings → General → avatar upload, a square logo image (≥128×128px). No API shortcut worth using here; just point the user at the setting if they haven't done it.
2. Description
One line, via API, no repo file touched:
enc=$(python3 -c "import urllib.parse; print(urllib.parse.quote('<namespace>/<project>', safe=''))")
glab api -X PUT "projects/$enc" --field "description=<one-liner>"
Name the stack, and for PrestaShop projects, the parent theme too (this is the detail that makes descriptions actually useful, not just decorative):
Gaudí & Barcelona Shop — PrestaShop 9.1.1 storefront (ElementFlow / stsitebuilder page builder)
Milagros Colombia — B2B PrestaShop 9.1 storefront, Panda child theme
Vives — PrestaShop 8 storefront, Panda child theme (viveschild)
Animalmax — job application portal (Laravel + Inertia + React)
How to find the real active theme — don't guess from folder names. A PS install usually ships several theme folders (classic/, panda/, hummingbird/, plus the client's custom one) but only one is active. Read the client's own theme's config/theme.yml for its parent: field to know what it's built on, then confirm it's the one actually in use — cross-check against CLAUDE.md if the repo has one, but don't trust it blindly: it can be stale. A quick sanity check that worked well: whichever theme directory has the most recent commit/mtime is almost always the one being actively developed, i.e. the active one.
cat themes/<client-theme>/config/theme.yml | grep parent
git log -1 --format=%ci -- themes/<theme-dir>
3. Topics (tags)
Also via API, same no-file-touched pattern:
glab api -X PUT "projects/$enc" --field "topics=prestashop,php,ecommerce"
Pick 3-5 real stack keywords, not generic filler. Examples that worked:
- PrestaShop storefront:
prestashop,php,ecommerce (+ b2b if it's a B2B store)
- Laravel + Inertia + React app:
laravel,php,react,typescript,inertia
4. Badges
Also API-only (POST projects/<id>/badges with name, link_url, image_url fields) — no PR needed.
First, check if the repo actually has CI (ls .gitlab-ci.yml in a clone, or glab api "projects/$enc/repository/files/.gitlab-ci.yml/raw?ref=<default_branch>"). This decides what kind of badge is honest to add:
-
No CI: static shields.io badges only — stack version, PHP version, theme. Nothing else to report, so don't add pipeline/coverage badges (they'd have nothing behind them).
glab api -X POST "projects/$enc/badges" \
--field "name=PrestaShop" \
--field "link_url=https://www.prestashop-project.org/" \
--field "image_url=https://img.shields.io/badge/PrestaShop-9.1.1-df0067?style=flat-square"
-
Has CI: add a real pipeline badge using GitLab's placeholder URL syntax (it resolves %{project_path}/%{default_branch} server-side, so it keeps working across renames/branch changes):
glab api -X POST "projects/$enc/badges" \
--field "name=Pipeline" \
--field "link_url=https://gitlab.com/%{project_path}/-/commits/%{default_branch}" \
--field "image_url=https://gitlab.com/%{project_path}/badges/%{default_branch}/pipeline.svg"
Only add a Coverage badge if the CI config actually produces a coverage report. Read the .gitlab-ci.yml jobs first — a security-scan-only pipeline (e.g. just a Trivy dependency scan) has no coverage number, so a coverage badge would sit at "unknown" forever, which reads as broken/misleading rather than honest. When in doubt, skip it and say why.
5. README
The hidden-README gotcha
GitLab only renders a project's overview from a non-dotfile named README/README.md/etc. A repo can have perfectly good docs sitting in a dot-prefixed file like .README.md that silently never displays on the project page. Always check for this before writing a new README from scratch:
ls -la
If found, git mv .README.md README.md and layer a short overview section on top — don't duplicate the existing content, it's usually solid dev documentation, just invisible.
Structure that's worked well
Project Name
============
[](link)
[](link)
One-line description of what this project is.
<p align="center">
<img src="docs/screenshot-home.webp" alt="..." width="900"/>
</p>
Stack
-----
- **Bullet list**, bold the key technology names
- Anything architecturally notable (fork-tracking strategy, override system, custom modules, page-builder module, etc.)
Environments
------------
- **Production/Pre:** <real URL> (note explicitly if it's down/in maintenance rather than silently skipping)
Local setup / Theme build
--------------------------
Only include this section if there's a real local dev flow worth documenting (e.g. Lando + a CSS build pipeline). Don't invent one.
Conventions
-----------
Pointer to CLAUDE.md if the repo has one — don't duplicate its content here.
See references/readme-template.md for a copy-pasteable skeleton.
Screenshot rule
If there's a reachable instance (local dev, staging, or production — in that order of preference for a NEW project; for an EXISTING project with a real deployed URL, use that URL, not a local one, see below), take a homepage screenshot and compress it before committing — a raw PNG from a browser screenshot tool is typically 1-1.5MB, which is heavy for a repo. Convert to WebP:
cwebp -q 82 screenshot.png -o docs/screenshot-home.webp
rm screenshot.png
If the reachable instance is down/in maintenance, say so in the README rather than silently omitting the screenshot — that's more honest than pretending there's nothing to show.
Never link local dev URLs in a README that has a real environment
If the project has a real staging/production URL, use that in the README — never a *.lndo.site or localhost link. Local dev links are only appropriate when the project genuinely has no deployed environment yet (e.g. it hasn't shipped anywhere).
Respect existing ownership
If a specific team member actively relies on and edits a project's README (ask if unsure — "does anyone use this doc day to day?"), don't touch the file even if it looks improvable. In that case, still do steps 2-4 (description/topics/badges) since those are GitLab metadata, not the README file, and don't step on anyone's workflow.
6. Applying a README change (existing project, no local clone yet)
git clone git@gl-cinetic:<namespace>/<project>.git /path/to/scratch
cd /path/to/scratch
git checkout -b docs/readme-<whatever>
git add README.md [docs/screenshot-home.webp]
git commit -m "docs: <conventional-commit-message-in-english>"
git push -u origin docs/readme-<whatever>
glab mr create --source-branch docs/readme-<whatever> --target-branch <default_branch> --title "..." --description "..."
Then decide on merge timing:
- No CI, or the repo owner has said "no CI, merge directly":
glab mr merge <id> --yes right away.
- Has real CI: it's still fine to merge a docs-only change immediately (nothing it touches affects the pipeline), but ask first if unsure — don't assume every repo works like the no-CI ones.
7. Org-hygiene things worth flagging (don't auto-fix)
These involve people/access, not just files — check and report, then ask before changing anything:
Report findings plainly (e.g. "14/14 members are Maintainer, branch protection is effectively decorative") and let the user decide the fix — this is their team's access model, not a call to make unilaterally.