Repository Template Skill 🧩
This skill enables adopting, configuring, or removing capabilities from the NHS England Tools Repository Template. Each capability is modular and can be applied independently.
Source Reference 📚
All implementation files are located in the assets/ subdirectory, which is a git subtree of the upstream repository template. When copying files to a target repository, use the contents from:
.github/skills/repository-template/assets/
For example, to adopt scripts/init.mk, copy from:
.github/skills/repository-template/assets/scripts/init.mk
to your target repository's scripts/init.mk.
⚠️ Distribution note: this SKILL file is mirrored verbatim across three homes - the NHS England shared GitHub Copilot prompt catalogue (similar to the Awesome GitHub Copilot Customizations), the upstream Repository Template, and every repository created from that template. Keep the wording identical in all locations, instead of editing per environment, detect where you are and resolve paths accordingly.
🤖 Assistant behaviour: when a user asks broad questions such as "repository template – describe how to use this skill", respond by summarising the capability list below, you must list all the capabilities in a tabular form as the next step and invite user to issue a follow-up prompt that names a specific capability plus an action (add, remove or improve) they want performed in their repository. This keeps replies actionable and focused on the modular building blocks.
AI assistants or automation should detect the active context before copying files. For a reliable conclusion, check the repository's git URL first (for example, git remote get-url origin). If it points to stefaniuk/repository-template, you are working inside the upstream template and can read directly from the project root.
Use the following checks after confirming the git URL:
- If
.github/skills/repository-template/assets/exists and contains files, use that subtree as the source (typical in the prompt catalogue or when the assets subtree is vendor-copied). - If the
assets/directory exists but is empty while template root files such asMakefile,scripts/, anddocs/are present, you are inside therepository-templateitself—read directly from the project root. - If neither case applies (for example, inside a repository that was generated from the template), treat the instructions as referring to files rooted in the current repository, because the template content has already been adopted there.
When in doubt, follow the Updating from the template repository workflow to pull fresh assets.
Critical Integration Rules 🚨
When adopting any capability from this skill, AI assistants must follow these rules:
Core Make System is a prerequisite — Most capabilities depend on make targets defined in
scripts/init.mk. Ifscripts/init.mkdoes not exist in the target repository, adopt the Core Make System first.Preserve
init.mkin full — Never partially copyscripts/init.mk. It contains interdependent targets (_install-dependencies,githooks-config,clean, etc.) that other capabilities rely on. Always copy the complete file.Ensure
include scripts/init.mk— The repository'sMakefilemust containinclude scripts/init.mknear the top. Without this, make targets frominit.mkare unavailable.Wire up
config::for dependencies — When adopting capabilities that require asdf-managed tools (pre-commit, gitleaks, etc.):Add the tool to
.tool-versionsEnsure the
Makefilehas aconfig::target that calls$(MAKE) _install-dependenciesExample:
config:: # Configure development environment @Configuration $(MAKE) _install-dependencies
Verify after adoption — Always run the verification commands listed in each capability section to confirm correct integration.
Scope and non-goals 🎯
Scope:
- Provide modular, copyable capabilities for repository setup, quality checks, tooling, and documentation
- Standardise local and CI workflows through Make targets and shared scripts
- Offer repeatable, deterministic setup via pinned tool versions
Non-goals:
- Full application scaffolding (use the feature-specific skills for that)
- Windows-native or PowerShell-first workflows
- Opinionated runtime or framework choices beyond the capabilities listed below
Compatibility 🧩
- Supported: macOS and Linux
- Supported via WSL: Windows (WSL2 with a Linux distribution)
- Not supported: Windows-native shells and PowerShell workflows
- Core tooling expectations: GNU Make 3.82+ and a POSIX-compatible shell
- Optional tooling: asdf for version pinning, Docker/Podman for container-related capabilities
Prerequisites ✅
- GNU Make 3.82+ (macOS users may need
brew install makeand to update$PATH) - Docker or Podman for container-related tasks
- asdf for pinned tool versions (optional if you manage versions another way)
- Python required for Git hooks
- jq for JSON processing in scripts
- GNU sed, GNU grep, GNU coreutils, GNU binutils for script compatibility (especially on macOS)
Troubleshooting 🛠️
- Make target not found: Ensure
scripts/init.mkexists andinclude scripts/init.mkis present near the top ofMakefile. - asdf command missing: Install asdf or remove asdf-specific steps from the capability you are adopting.
- Pre-commit hooks do not run: Run
make githooks-configand confirm.git/hooks/pre-commitexists. - Docker checks fail: Confirm Docker/Podman is installed and running, then rerun
make docker-lint.
Upgrade guidance 🔄
When updating a repository that already uses this template:
- Pull fresh assets using
./.github/skills/repository-template/scripts/git-clone-repository-template.sh - Compare changed files and copy only the capabilities you use
- Re-run
make configand any verification steps for the updated capabilities - Review CI workflows and
.tool-versionsfor version pin changes - You can ask GitHub Copilot to use this skill to perform the upgrade for you
Security considerations 🔐
- Do not commit secrets; rely on secret scanning and ignore lists for known false positives
- Keep CI credentials scoped to the minimum required permissions
- Treat
.tool-versionsand configuration files as security-sensitive inputs - Review new third-party tools before adoption and pin versions where possible
FAQ ❓
Does this support Windows? No. Use WSL2 with a Linux distribution if you are on Windows.
Can I adopt a single capability? Yes. Each capability is modular; copy only the files it references.
Do I need asdf? Only for capabilities that rely on version pinning; it is optional otherwise.
Where do the source files live?
They are in the assets/ subtree under this skill.
Quick Reference 🧠
| Capability | Purpose | Key Files |
|---|---|---|
| Core Make System | Standardised task runner | Makefile, scripts/init.mk |
| Pre-commit Hooks | Git hooks framework | scripts/config/pre-commit.yaml, scripts/quality/ |
| Secret Scanning | Prevent credential leaks | scripts/quality/scan-secrets.sh, scripts/config/gitleaks.toml |
| File Format Checking | Consistent file formatting | .editorconfig, scripts/quality/check-file-format.sh |
| Markdown Linting | Documentation quality | scripts/quality/check-markdown-format.sh, scripts/config/markdownlint.yaml |
| Markdown Link Checking | Validate Markdown links | scripts/quality/check-markdown-links.sh, scripts/config/lychee.toml |
| Shell Script Linting | Bash quality checks | scripts/quality/check-shell-lint.sh |
| Docker Support | Container build/run/lint | scripts/docker/, scripts/config/hadolint.yaml |
| GitHub Actions CI/CD | Pipeline workflows | .github/workflows/, .github/actions/ |
| Dependabot | Automated dependency updates | .github/dependabot.yaml |
| VS Code Integration | Editor configuration | .vscode/, project.code-workspace |
| Tool Version Management | Reproducible toolchain | .tool-versions |
| GitHub Repository Templates | Issue/PR/security templates | .github/ISSUE_TEMPLATE/, .github/pull_request_template.md |
| Documentation Structure | ADRs and guides | docs/adr/, docs/guides/ |
Capabilities 🧰
1. Core Make System (GNU Make)
Purpose: Provides a standardised task runner with self-documenting help, common targets, and extensibility.
Benefits: A single, well-documented entry point lowers cognitive load and aligns local and CI workflows. It improves reproducibility and makes automation safer because commands are deterministic and discoverable.
Problem it solves: Teams often grow ad-hoc scripts and tribal knowledge for routine tasks, which leads to inconsistent results and slow onboarding.
How it solves it: The core Makefile and init module provide stable targets, shared defaults, and explicit configuration hooks, so every engineer and pipeline uses the same contract.
Dependencies: GNU Make 3.82+
Source files (in assets/):
Makefile— Project-specific targets (customise this)scripts/init.mk— Common targets and infrastructure (do not edit)
Key make targets:
make help # Show all available targets with descriptions
make config # Configure development environment
make clean # Remove generated files
make list-variables # Debug: show all make variables
make scan-secrets # Scan for secrets
make check-file-format # Check file format compliance
make check-markdown-format # Check Markdown formatting
make check-markdown-links # Check Markdown links
make check-shell-lint # Lint shell scripts
make version-create-effective-file # Create .version from VERSION
Template project targets (from assets/Makefile):
make env— Set up project environment (placeholder)make deps— Install project dependencies (placeholder)make format— Auto-format code (placeholder)make lint-file-format,make lint-markdown-format,make lint-markdown-links,make lint-shell— Run individual checksmake lint— Runs all four lint targets abovemake typecheck,make test,make build,make publish,make deploy— Project-specific placeholders you implement
To adopt:
Copy
assets/Makefileandassets/scripts/init.mkto your repositoryEnsure
Makefilecontainsinclude scripts/init.mknear the top (after any variable definitions)Customise the
Makefilewith your project-specific targetsAdd
@Pipeline,@Operations,@Configuration,@Development,@Testing,@Quality, or@Othersannotations to target comments for categorisationAdd a
config::target that calls$(MAKE) _install-dependenciesto ensure asdf tools are installed:config:: # Configure development environment @Configuration $(MAKE) _install-dependencies
Essential make targets from init.mk (do not remove or modify):
| Target | Purpose |
|---|---|
help |
Self-documenting target list |
config |
Base configuration (extended via config::) |
clean |
Base cleanup (extended via clean::) |
scan-secrets |
Scan for secrets using the gitleaks wrapper |
check-file-format |
Check EditorConfig compliance |
check-markdown-format |
Check Markdown formatting |
check-markdown-links |
Check Markdown links |
check-shell-lint |
Lint shell scripts (fails on errors, excludes .github/skills/repository-template/ and .specify/ paths) |
version-create-effective-file |
Create the .version file from VERSION placeholders |
_install-dependencies |
Install all tools from .tool-versions via asdf |
_install-dependency |
Install a single asdf tool |
githooks-config |
Install pre-commit hooks |
githooks-run |
Run all pre-commit hooks |
Verification (run after adoption):
# Check make is available and version is 3.82+
make --version | head -1
# Verify help target works and shows categorised output
make help
# Expected: Exit code 0, output includes target names with descriptions
# Success indicator: Output contains "help" target and category headers
To remove: Delete Makefile and scripts/init.mk
2. Pre-commit Hooks
Purpose: Framework for running quality checks before commits using the pre-commit tool.
Benefits: Early feedback reduces rework and keeps the main branch clean. It also shortens CI cycles by catching simple issues before a push.
Problem it solves: Quality checks run late or inconsistently, so defects slip into review and break pipelines.
How it solves it: Pre-commit runs the same repo-defined checks locally through Make targets and shared scripts, ensuring the local and CI gates are aligned.
Dependencies: Python, pre-commit (pip install pre-commit), make
Source files (in assets/):
scripts/config/pre-commit.yaml— Hook definitionsscripts/quality/scan-secrets.sh— Secret scan hook wrapperscripts/quality/check-file-format.sh— File format hook wrapperscripts/quality/check-markdown-format.sh— Markdown format hook wrapperscripts/quality/check-markdown-links.sh— Markdown link hook wrapperscripts/config/gitleaks.toml— Gitleaks configurationscripts/config/.gitleaksignore— Gitleaks ignore listscripts/config/editorconfig-checker.json— EditorConfig checker configurationscripts/config/markdownlint.yaml— Markdownlint configurationscripts/config/.markdownlintignore— Markdownlint ignore listscripts/config/lychee.toml— Lychee configuration
Configuration:
make githooks-config # Install hooks
make githooks-run # Run all hooks manually
Available hooks (each can be enabled/disabled in pre-commit.yaml):
scan-secrets— Gitleaks secret scanningcheck-file-format— EditorConfig compliancecheck-markdown-format— Markdown lintingcheck-markdown-links— Markdown link checking
To adopt:
Prerequisite: Ensure Core Make System is already adopted
Copy
scripts/config/pre-commit.yamlCopy
scripts/quality/andscripts/config/(the hooks call make targets backed by these scripts and configs)Add
pre-committo.tool-versions(e.g.,pre-commit 4.5.1)Ensure
Makefilehasconfig::target that calls$(MAKE) _install-dependencies:config:: # Configure development environment @Configuration $(MAKE) _install-dependenciesRun
make configto install pre-commit via asdfRun
make githooks-configto install the git hooks
Verification (run after adoption):
# Check pre-commit is installed
pre-commit --version
# Verify hooks are configured
test -f .git/hooks/pre-commit && echo "Hooks installed" || echo "Hooks not installed"
# Run hooks manually (should complete without errors on clean repo)
pre-commit run --config scripts/config/pre-commit.yaml --all-files
# Expected: Exit code 0 if all checks pass, non-zero if issues found
# Success indicator: "Passed" or "Skipped" for each hook
To remove:
- Run
pre-commit uninstall - Delete
scripts/config/pre-commit.yamland remove any hook references to it
3. Secret Scanning (Gitleaks)
Purpose: Prevent hardcoded secrets from being committed to the repository.
Benefits: It reduces the risk of credential exposure and downstream incidents, and supports compliance expectations for secret hygiene.
Problem it solves: Manual review cannot reliably spot secrets, and a single leak can force rotation and downtime.
How it solves it: Gitleaks scans staged changes and history with a tuned rule set and allowlist, producing deterministic results that can be enforced in hooks and CI.
Dependencies: Gitleaks (native or Docker)
Source files (in assets/):
scripts/quality/scan-secrets.sh— Scanner wrapperscripts/config/gitleaks.toml— Gitleaks configurationscripts/config/.gitleaksignore— Ignore file for false positives
Optional baseline file:
- If you create
scripts/config/.gitleaks-baseline.json, the wrapper will include it automatically.
Configuration (scripts/config/gitleaks.toml):
- Extends default Gitleaks rules
- Custom IPv4 detection with private network allowlist
- Excludes lock files (
poetry.lock,yarn.lock)
Check modes:
check=staged-changes ./scripts/quality/scan-secrets.sh # Pre-commit (default)
check=last-commit ./scripts/quality/scan-secrets.sh # Last commit only
check=whole-history ./scripts/quality/scan-secrets.sh # Full repository history
Usage:
make scan-secrets check=whole-history
check=staged-changes ./scripts/quality/scan-secrets.sh
To adopt:
Copy
scripts/quality/scan-secrets.sh,scripts/config/gitleaks.toml, andscripts/config/.gitleaksignoreAdd
gitleaksto.tool-versions(e.g.,gitleaks 8.30.0) for native executionOptionally add Docker image entry to
.tool-versionsfor Docker fallback:# docker/ghcr.io/gitleaks/gitleaks v8.30.0@sha256:691af3c7c5a48b16f187ce3446d5f194838f91238f27270ed36eef6359a574d9 # SEE: https://github.com/gitleaks/gitleaks/pkgs/container/gitleaksRun
asdf installto install gitleaks (if using native)Add to
pre-commit.yamlor run standalone
Verification (run after adoption):
# Check gitleaks is available
gitleaks version
# Run secret scan on staged changes (default mode)
./scripts/quality/scan-secrets.sh
# Or scan the whole repository history
check=whole-history ./scripts/quality/scan-secrets.sh
# Alternative: run gitleaks directly
gitleaks detect --config scripts/config/gitleaks.toml --source . --verbose --redact
# Expected: Exit code 0 if no secrets found
# Success indicator: "no leaks found" or empty output
To remove: Delete the files and remove from pre-commit.yaml
4. File Format Checking (EditorConfig)
Purpose: Ensure consistent file formatting (indentation, line endings, charset) across the codebase.
Benefits: Consistent formatting improves readability, reduces merge noise, and keeps diffs focused on behaviour.
Problem it solves: Inconsistent whitespace and line endings cause churn and make code review harder.
How it solves it: EditorConfig defines the contract and the checker enforces it over the chosen file scope, so formatting stays stable across editors and platforms.
Dependencies: editorconfig-checker (native or Docker)
Source files (in assets/):
.editorconfig— Format rulesscripts/quality/check-file-format.sh— Checker wrapperscripts/config/editorconfig-checker.json— Checker configuration
Configuration (.editorconfig):
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
[*.py]
indent_size = 4
[{Makefile,*.mk}]
indent_style = tab
Check modes:
check=all ./scripts/quality/check-file-format.sh # All files
check=staged-changes ./scripts/quality/check-file-format.sh # Staged only
check=working-tree-changes ./scripts/quality/check-file-format.sh # Working tree only
check=branch ./scripts/quality/check-file-format.sh # Changes since branching
Extra options:
BRANCH_NAME=origin/mainto override the branch used forcheck=branchdry_run=trueto list issues without failing the checkFORCE_USE_DOCKER=trueto force Docker executionVERBOSE=trueto show commands
Usage:
make check-file-format check=all
check=branch ./scripts/quality/check-file-format.sh
To adopt:
Copy
.editorconfig,scripts/quality/check-file-format.sh, andscripts/config/editorconfig-checker.jsonAdd Docker image entry to
.tool-versionsfor editorconfig-checker:# docker/mstruebing/editorconfig-checker v3.6@sha256:af556694c3eb0a16b598efbe84c1171d40dfb779fdac6f01b89baedde065556f # SEE: https://hub.docker.com/r/mstruebing/editorconfig-checker/tagsInstall VS Code extension
editorconfig.editorconfig
Verification (run after adoption):
# Check editorconfig-checker is available
editorconfig-checker --version || ec --version
# Run format check on all files
check=all ./scripts/quality/check-file-format.sh
# Alternative: run checker directly
editorconfig-checker -config scripts/config/editorconfig-checker.json
# Expected: Exit code 0 if all files comply
# Success indicator: No output (silent success) or "No issues found"
To remove: Delete the files
5. Markdown Linting (markdownlint-cli)
Purpose: Enforce consistent Markdown formatting and best practices.
Benefits: It keeps documentation consistent and easy to render across tooling, improving trust in the docs.
Problem it solves: Markdown style drift leads to broken layouts, noisy diffs, and unclear guidance.
How it solves it: markdownlint applies a shared ruleset and ignore list, giving a repeatable check in local workflows and CI.
Dependencies: markdownlint-cli (native or Docker)
Source files (in assets/):
scripts/quality/check-markdown-format.sh— Linter wrapperscripts/config/markdownlint.yaml— Rule configurationscripts/config/.markdownlintignore— Ignore patterns
Configuration (scripts/config/markdownlint.yaml):
MD010: # Allow hard tabs in makefile code blocks
ignore_code_languages: [makefile]
MD013: false # Disable line length
MD024: # Allow duplicate headings in siblings
siblings_only: true
MD029:
style: ordered
MD033: false # Allow inline HTML
MD036: false # Allow emphasis instead of headings
MD041: false # Allow files without an H1
Check modes:
check=all ./scripts/quality/check-markdown-format.sh # All markdown files
check=staged-changes ./scripts/quality/check-markdown-format.sh # Staged only
check=working-tree-changes ./scripts/quality/check-markdown-format.sh # Working tree only
check=branch ./scripts/quality/check-markdown-format.sh # Changes since branching
Extra options:
BRANCH_NAME=origin/mainto override the branch used forcheck=branchFORCE_USE_DOCKER=trueto force Docker executionVERBOSE=trueto show commands
Usage:
make check-markdown-format check=all
check=branch ./scripts/quality/check-markdown-format.sh
To adopt:
Copy
scripts/quality/check-markdown-format.sh,scripts/config/markdownlint.yaml, andscripts/config/.markdownlintignoreAdd Docker image entry to
.tool-versionsfor markdownlint-cli:# docker/ghcr.io/igorshubovych/markdownlint-cli v0.47.0@sha256:9f06c8c9a75aa08b87b235b66d618f7df351f09f08faf703177f670e38ee6511 # SEE: https://github.com/igorshubovych/markdownlint-cli/pkgs/container/markdownlint-cliInstall VS Code extension
davidanson.vscode-markdownlint
Verification (run after adoption):
# Check markdownlint is available
markdownlint --version
# Run on all markdown files
check=all ./scripts/quality/check-markdown-format.sh
# Alternative: run markdownlint directly
markdownlint --config scripts/config/markdownlint.yaml --ignore-path scripts/config/.markdownlintignore "**/*.md"
# Expected: Exit code 0 if all files pass
# Success indicator: No output (silent success)
To remove: Delete the files
6. Markdown Link Checking (Lychee)
Purpose: Validate Markdown links for broken or unreachable references.
Benefits: Reliable links keep docs usable and reduce support overhead, especially for onboarding and audits.
Problem it solves: Link rot and typos silently break guidance, which wastes time and undermines confidence.
How it solves it: Lychee scans Markdown files using a defined configuration and scope, so broken links are detected early and consistently.
Dependencies: Lychee (native or Docker)
Source files (in assets/):
scripts/quality/check-markdown-links.sh— Link checker wrapperscripts/config/lychee.toml— Lychee configuration
Configuration (scripts/config/lychee.toml):
- Uses a tuned set of defaults for reliability and reduced noise
- Centralises link exclusions and timeouts for consistent results
Check modes:
check=all ./scripts/quality/check-markdown-links.sh # All markdown files
check=staged-changes ./scripts/quality/check-markdown-links.sh # Staged only
check=working-tree-changes ./scripts/quality/check-markdown-links.sh # Working tree only
check=branch ./scripts/quality/check-markdown-links.sh # Changes since branching
Extra options:
BRANCH_NAME=origin/mainto override the branch used forcheck=branchFORCE_USE_DOCKER=trueto force Docker executionVERBOSE=trueto show commands
Usage:
make check-markdown-links check=all
check=branch ./scripts/quality/check-markdown-links.sh
To adopt:
Copy
scripts/quality/check-markdown-links.shandscripts/config/lychee.tomlOptionally pin the Docker image in
.tool-versionsusing the standard format:# docker/lycheeverse/lychee <tag>@<digest> # SEE: https://github.com/lycheeverse/lychee
Verification (run after adoption):
# Check lychee is available
lychee --version
# Run on all markdown files
check=all ./scripts/quality/check-markdown-links.sh
# Alternative: run lychee directly
lychee --config scripts/config/lychee.toml --no-progress --quiet "**/*.md"
# Expected: Exit code 0 if all links are valid
# Success indicator: No output (silent success)
To remove: Delete the files
7. Shell Script Linting (ShellCheck)
Purpose: Static analysis for shell scripts to catch common bugs and enforce best practices.
Benefits: Static analysis catches common shell pitfalls and improves script reliability, which is critical for automation and CI.
Problem it solves: Shell scripts fail in subtle ways due to quoting, globbing, and error handling quirks.
How it solves it: ShellCheck runs via a wrapper that standardises execution and tooling, producing consistent findings for developers and pipelines.
Dependencies: ShellCheck (native or Docker)
Source files (in assets/):
scripts/quality/check-shell-lint.sh— ShellCheck wrapper
Configuration:
- Uses ShellCheck default rules with no repo-specific overrides
- Runs natively when available, with a Docker fallback for parity
Check modes:
- All scripts:
make check-shell-lint(scans every*.shin the repo, excluding.github/skills/repository-template/and.specify/paths) - Single script:
file=path/to/script.sh ./scripts/quality/check-shell-lint.sh
Extra options:
FORCE_USE_DOCKER=trueto force Docker executionVERBOSE=trueto show commands
Usage:
file=path/to/script.sh ./scripts/quality/check-shell-lint.sh
make check-shell-lint # Lint all .sh files in the repo
Verification (run after adoption):
# Check shellcheck is available
shellcheck --version
# Run on a specific script
file=scripts/quality/check-shell-lint.sh ./scripts/quality/check-shell-lint.sh
# Alternative: run shellcheck directly
make check-shell-lint
# Expected: Exit code 0 if no issues, non-zero with error details if issues found
# Success indicator: No output (silent success) or warnings/errors listed
To adopt:
Copy
scripts/quality/check-shell-lint.shAdd Docker image entry to
.tool-versionsfor shellcheck:# docker/koalaman/shellcheck v0.11.0@sha256:61862eba1fcf09a484ebcc6feea46f1782532571a34ed51fedf90dd25f925a8d # SEE: https://hub.docker.com/r/koalaman/shellcheck/tags
To remove: Delete the file
8. Docker Support (Docker/Podman)
Purpose: Build, lint, run, and manage Docker images with standardised metadata.
Benefits: Standardised build and lint steps improve image security, reproducibility, and operational consistency.
Problem it solves: Ad-hoc Dockerfiles and build commands lead to drift, insecure defaults, and slow debugging.
How it solves it: Shared make targets and libraries encode best practices, apply metadata, and enforce hadolint checks with version-pinned tooling.
Dependencies: Docker, hadolint (for linting)
Source files (in assets/):
scripts/docker/docker.mk— Make targetsscripts/docker/docker.lib.sh— Shell functions libraryscripts/docker/dockerfile-linter.sh— Hadolint wrapperscripts/docker/Dockerfile.metadata— OCI label templatescripts/config/hadolint.yaml— Hadolint configurationscripts/docker/dgoss.sh— Container testing with dgossscripts/docker/tests/— Docker test fixtures
Make targets:
make docker-bake-dockerfile # Create Dockerfile.effective
make docker-build # Build image with metadata
make docker-lint # Lint Dockerfile with hadolint
make docker-push # Push to registry
make docker-run # Run container
make docker-shellscript-lint # Lint Docker module shell scripts
make docker-test-suite-run # Run Docker test suite
Features:
- Automatic
Dockerfile.effectivegeneration with version baking - Version metadata generation via
.versioncreated fromVERSIONplaceholders - OCI-compliant image labels (title, version, git info, build date)
- Trusted registry allowlist in hadolint config
- Test suite support with dgoss
- Docker image version pinning via
.tool-versions— see Tool Version Management (asdf) for the extended format
Docker image versioning:
Docker image versions can be pinned in .tool-versions using an extended comment format. The docker-get-image-version-and-pull function in docker.lib.sh parses these entries, pulls images by digest for reproducibility, and tags them locally for caching.
# docker/ghcr.io/gitleaks/gitleaks v8.30.0@sha256:691af3c7c5a48b16f187ce3446d5f194838f91238f27270ed36eef6359a574d9 # SEE: https://github.com/gitleaks/gitleaks/pkgs/container/gitleaks
See section 12 for full format documentation.
To adopt:
Copy the entire
scripts/docker/directoryCopy
scripts/config/hadolint.yamlAdd Docker image entry to
.tool-versionsfor hadolint:# docker/hadolint/hadolint 2.14.0-alpine@sha256:7aba693c1442eb31c0b015c129697cb3b6cb7da589d85c7562f9deb435a6657c # SEE: https://hub.docker.com/r/hadolint/hadolint/tagsCreate your Dockerfile in
infrastructure/images/Optionally add additional Docker image pins to
.tool-versions
Verification (run after adoption):
# Check Docker is available
docker --version
# Check hadolint is available (for Dockerfile linting)
hadolint --version || docker run --rm hadolint/hadolint hadolint --version
# Lint a Dockerfile
file=path/to/Dockerfile ./scripts/docker/dockerfile-linter.sh
# Alternative: run hadolint directly
hadolint --config scripts/config/hadolint.yaml path/to/Dockerfile
# Verify docker make targets exist
make help | grep -E "docker-bake-dockerfile|docker-build|docker-lint|docker-push|docker-run|docker-shellscript-lint|docker-test-suite-run"
# Expected: Exit code 0 if Dockerfile is valid
# Success indicator: No lint errors, make targets visible in help
To remove: Delete scripts/docker/ and remove include scripts/docker/docker.mk from scripts/init.mk
9. GitHub Actions CI/CD
Purpose: Multi-stage CI/CD pipeline with reusable workflows and composite actions.
Benefits: A predictable pipeline enforces quality gates and makes delivery repeatable, improving reliability and auditability.
Problem it solves: Manual or inconsistent CI steps cause flaky results and slow down releases.
How it solves it: Reusable workflows and composite actions provide a single source of truth for stages and checks, reducing duplication and drift.
Dependencies: GitHub repository with Actions enabled; GitHub CLI available on runners (used to detect pull requests); contents: read and pull-requests: read permissions for the metadata job
Source files (in assets/):
assets/.github/workflows/cicd-1-pull-request.yaml— Main PR workflowassets/.github/workflows/cicd-2-publish.yaml— Publish workflowassets/.github/workflows/cicd-3-deploy.yaml— Deployment workflowassets/.github/workflows/stage-1-commit.yaml— Commit stage (quality checks)assets/.github/workflows/stage-2-test.yaml— Test stageassets/.github/workflows/stage-3-build.yaml— Build stageassets/.github/workflows/stage-4-acceptance.yaml— Acceptance stageassets/.github/actions/— Composite actions for each check
Metadata job:
- Captures build timestamps, epoch, Node.js/Python versions (when present in
.tool-versions), and generates.version - Detects whether a pull request exists using
gh pr listto gate downstream stages
Pipeline stages:
- Commit stage (~2 min): Secret scan, file format, Markdown format, Markdown links
- Test stage (~5 min): Unit tests (
make testplaceholder) - Build stage (~3 min): Artefact build placeholders (runs only when a PR exists or on opened/reopened PR events)
- Acceptance stage (~10 min): Environment setup, contract/security/UI/performance/integration/accessibility/load tests, then teardown (runs only when a PR exists or on opened/reopened PR events)
Composite actions (.github/actions/):
scan-secrets/check-file-format/check-markdown-format/check-markdown-links/
To adopt:
- Copy
.github/workflows/and.github/actions/ - Customise stages for your project
- Configure secrets in GitHub repository settings
Verification (run after adoption):
# Verify workflow files exist
ls -la .github/workflows/*.yaml
# Validate YAML syntax (requires yq or python)
yq eval '.' .github/workflows/cicd-1-pull-request.yaml > /dev/null && echo "Valid YAML"
# Check for composite actions
ls -la .github/actions/
# Verify workflow references valid actions
grep -r "uses:.*\.github/actions/" .github/workflows/
# Expected: Workflow files present, valid YAML syntax
# Success indicator: Files exist, no YAML parse errors
# Note: Full verification requires pushing to GitHub and observing Actions tab
To remove: Delete .github/workflows/ and .github/actions/
10. Dependabot
Purpose: Automated dependency update pull requests for multiple package ecosystems.
Benefits: Automated updates reduce security exposure and maintenance effort, while keeping changes reviewable.
Problem it solves: Dependencies age quickly, and manual updates are often missed or deferred.
How it solves it: Dependabot raises scheduled PRs per ecosystem so teams can review and merge controlled updates.
Dependencies: GitHub repository with Dependabot enabled
Source files (in assets/):
- [
.github/dependabot.yaml](assets/.github/de
…(truncated)