Skip to content

Commit 188855e

Browse files
feat(changelog): add git-cliff to standards and internal Makefile copies
Update makefile-contract.md with `make changelog` target (10th public target), update universal.md with git-cliff tool docs, sync all three internal Makefile copies with changelog/init changes. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8545e4f commit 188855e

5 files changed

Lines changed: 646 additions & 13 deletions

File tree

dev-toolchain/Makefile

Lines changed: 202 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ HAS_ANSIBLE := $(filter ansible,$(LANGUAGES))
4343
# ---------------------------------------------------------------------------
4444
# .PHONY declarations
4545
# ---------------------------------------------------------------------------
46-
.PHONY: help build lint format test security scan docs check install-hooks
47-
.PHONY: _lint _format _test _security _scan _docs _check _check-config
46+
.PHONY: help build lint format test security scan docs changelog check install-hooks init
47+
.PHONY: _lint _format _test _security _scan _docs _changelog _check _check-config _init
4848

4949
# ===========================================================================
5050
# Public targets (run on host, delegate to Docker container)
@@ -59,6 +59,9 @@ help: ## Show this help
5959
build: ## Build the container image locally
6060
docker build -t $(DEVRAIL_IMAGE):$(DEVRAIL_TAG) .
6161

62+
changelog: ## Generate CHANGELOG.md from conventional commits
63+
$(DOCKER_RUN) make _changelog
64+
6265
check: ## Run all checks (lint, format, test, security, scan, docs)
6366
$(DOCKER_RUN) make _check
6467

@@ -89,6 +92,9 @@ install-hooks: ## Install pre-commit hooks
8992
@pre-commit install --hook-type commit-msg
9093
@echo "Pre-commit hooks installed successfully. Hooks will run on every commit."
9194

95+
init: ## Scaffold config files for declared languages
96+
$(DOCKER_RUN) make _init
97+
9298
lint: ## Run all linters
9399
$(DOCKER_RUN) make _lint
94100

@@ -493,6 +499,7 @@ _docs: _check-config
493499
fi; \
494500
_tv trivy "trivy --version"; \
495501
_tv gitleaks "gitleaks version"; \
502+
_tv git-cliff "git-cliff --version"; \
496503
printf '}}\n'; \
497504
} > .devrail-output/tool-versions.json; \
498505
generators="$${generators}\"tool-versions\","; \
@@ -505,6 +512,199 @@ _docs: _check-config
505512
fi; \
506513
exit $$overall_exit
507514

515+
# --- _changelog: generate CHANGELOG.md from conventional commits ---
516+
_changelog: _check-config
517+
@start_time=$$(date +%s%3N); \
518+
config=""; \
519+
if [ -f "cliff.toml" ]; then \
520+
config="cliff.toml"; \
521+
elif [ -f "/opt/devrail/config/cliff.toml" ]; then \
522+
config="/opt/devrail/config/cliff.toml"; \
523+
fi; \
524+
if [ -z "$$config" ]; then \
525+
echo '{"target":"changelog","status":"error","error":"no cliff.toml found","exit_code":2}'; \
526+
exit 2; \
527+
fi; \
528+
if ! git rev-parse --git-dir >/dev/null 2>&1; then \
529+
echo '{"target":"changelog","status":"error","error":"not a git repository","exit_code":2}'; \
530+
exit 2; \
531+
fi; \
532+
git-cliff --config "$$config" --output CHANGELOG.md; \
533+
cl_exit=$$?; \
534+
end_time=$$(date +%s%3N); \
535+
duration=$$((end_time - start_time)); \
536+
if [ $$cl_exit -eq 0 ]; then \
537+
echo "{\"target\":\"changelog\",\"status\":\"pass\",\"duration_ms\":$$duration,\"config\":\"$$config\",\"output\":\"CHANGELOG.md\"}"; \
538+
else \
539+
echo "{\"target\":\"changelog\",\"status\":\"fail\",\"duration_ms\":$$duration,\"config\":\"$$config\",\"exit_code\":$$cl_exit}"; \
540+
exit $$cl_exit; \
541+
fi
542+
543+
# --- _init: scaffold config files for declared languages ---
544+
_init: _check-config
545+
@created=""; \
546+
skipped=""; \
547+
scaffold() { \
548+
_f="$$1"; shift; \
549+
if [ ! -f "$$_f" ]; then \
550+
printf '%s\n' "$$@" > "$$_f"; \
551+
created="$${created}\"$$_f\","; \
552+
else \
553+
skipped="$${skipped}\"$$_f\","; \
554+
fi; \
555+
}; \
556+
scaffold .editorconfig \
557+
'root = true' \
558+
'' \
559+
'[*]' \
560+
'charset = utf-8' \
561+
'end_of_line = lf' \
562+
'insert_final_newline = true' \
563+
'trim_trailing_whitespace = true' \
564+
'indent_style = space' \
565+
'indent_size = 2' \
566+
'' \
567+
'[Makefile]' \
568+
'indent_style = tab' \
569+
'' \
570+
'[*.py]' \
571+
'indent_size = 4' \
572+
'' \
573+
'[*.sh]' \
574+
'indent_size = 2'; \
575+
if [ -f "/opt/devrail/config/cliff.toml" ] && [ ! -f "cliff.toml" ]; then \
576+
cp /opt/devrail/config/cliff.toml cliff.toml; \
577+
created="$${created}\"cliff.toml\","; \
578+
elif [ -f "cliff.toml" ]; then \
579+
skipped="$${skipped}\"cliff.toml\","; \
580+
fi; \
581+
if [ -n "$(HAS_PYTHON)" ]; then \
582+
scaffold ruff.toml \
583+
'line-length = 120' \
584+
'target-version = "py311"' \
585+
'' \
586+
'[lint]' \
587+
'select = ["E", "W", "F", "I", "UP", "B", "S", "C4", "SIM"]' \
588+
'' \
589+
'[format]' \
590+
'quote-style = "double"' \
591+
'indent-style = "space"'; \
592+
fi; \
593+
if [ -n "$(HAS_BASH)" ]; then \
594+
scaffold .shellcheckrc \
595+
'shell=bash' \
596+
'enable=all'; \
597+
fi; \
598+
if [ -n "$(HAS_TERRAFORM)" ]; then \
599+
scaffold .tflint.hcl \
600+
'config {' \
601+
' call_module_type = "local"' \
602+
'}' \
603+
'' \
604+
'plugin "terraform" {' \
605+
' enabled = true' \
606+
' preset = "recommended"' \
607+
'}'; \
608+
fi; \
609+
if [ -n "$(HAS_ANSIBLE)" ]; then \
610+
scaffold .ansible-lint \
611+
'profile: production' \
612+
'' \
613+
'exclude_paths:' \
614+
' - .cache/' \
615+
' - .github/' \
616+
' - .gitlab/' \
617+
'' \
618+
'skip_list:' \
619+
' - yaml[truthy]' \
620+
'' \
621+
'warn_list:' \
622+
' - experimental'; \
623+
fi; \
624+
if [ -n "$(HAS_RUBY)" ]; then \
625+
scaffold .rubocop.yml \
626+
'AllCops:' \
627+
' TargetRubyVersion: 3.1' \
628+
' NewCops: enable' \
629+
' Exclude:' \
630+
' - "db/schema.rb"' \
631+
' - "bin/**/*"' \
632+
' - "vendor/**/*"' \
633+
' - "node_modules/**/*"' \
634+
'' \
635+
'Style/Documentation:' \
636+
' Enabled: false' \
637+
'' \
638+
'Metrics/BlockLength:' \
639+
' Exclude:' \
640+
' - "spec/**/*"' \
641+
'' \
642+
'Layout/LineLength:' \
643+
' Max: 120'; \
644+
scaffold .reek.yml \
645+
'exclude_paths:' \
646+
' - vendor' \
647+
' - db/schema.rb' \
648+
' - bin' \
649+
'' \
650+
'detectors:' \
651+
' IrresponsibleModule:' \
652+
' enabled: false'; \
653+
scaffold .rspec \
654+
'--require spec_helper' \
655+
'--format documentation' \
656+
'--color'; \
657+
fi; \
658+
if [ -n "$(HAS_GO)" ]; then \
659+
scaffold .golangci.yml \
660+
'version: "2"' \
661+
'' \
662+
'linters:' \
663+
' enable:' \
664+
' - errcheck' \
665+
' - govet' \
666+
' - staticcheck' \
667+
' - gosec' \
668+
' - ineffassign' \
669+
' - unused' \
670+
' - gocritic' \
671+
' - gofumpt' \
672+
' - misspell' \
673+
' - revive' \
674+
'' \
675+
'issues:' \
676+
' exclude-dirs:' \
677+
' - vendor' \
678+
' - node_modules'; \
679+
fi; \
680+
if [ -n "$(HAS_JAVASCRIPT)" ]; then \
681+
scaffold eslint.config.js \
682+
'import eslint from "@eslint/js";' \
683+
'import tseslint from "typescript-eslint";' \
684+
'' \
685+
'export default tseslint.config(' \
686+
' eslint.configs.recommended,' \
687+
' tseslint.configs.recommended,' \
688+
' {' \
689+
' ignores: ["node_modules/", "dist/", "build/", "coverage/"],' \
690+
' }' \
691+
');'; \
692+
scaffold .prettierrc \
693+
'{' \
694+
' "semi": true,' \
695+
' "singleQuote": false,' \
696+
' "trailingComma": "es5",' \
697+
' "printWidth": 80,' \
698+
' "tabWidth": 2' \
699+
'}'; \
700+
scaffold .prettierignore \
701+
'node_modules/' \
702+
'dist/' \
703+
'build/' \
704+
'coverage/'; \
705+
fi; \
706+
echo "{\"target\":\"init\",\"created\":[$${created%,}],\"skipped\":[$${skipped%,}]}"
707+
508708
# --- _check: orchestrate all targets ---
509709
_check: _check-config
510710
@overall_exit=0; \

0 commit comments

Comments
 (0)