@@ -40,6 +40,7 @@ HAS_ANSIBLE := $(filter ansible,$(LANGUAGES))
4040HAS_RUBY := $(filter ruby,$(LANGUAGES ) )
4141HAS_GO := $(filter go,$(LANGUAGES ) )
4242HAS_JAVASCRIPT := $(filter javascript,$(LANGUAGES ) )
43+ HAS_RUST := $(filter rust,$(LANGUAGES ) )
4344
4445# ---------------------------------------------------------------------------
4546# .PHONY declarations
@@ -255,6 +256,21 @@ _lint: _check-config
255256 exit $$ overall_exit; \
256257 fi ; \
257258 fi ; \
259+ if [ -n " $( HAS_RUST) " ]; then \
260+ ran_languages=" $$ {ran_languages}\" rust\" ," ; \
261+ rs_files=$$(find . -name '*.rs' -not -path './.git/*' -not -path './vendor/*' -not -path './target/*' 2>/dev/null ) ; \
262+ if [ -n " $$ rs_files" ]; then \
263+ cargo clippy --all-targets --all-features -- -D warnings || { overall_exit=1; failed_languages=" $$ {failed_languages}\" rust\" ," ; }; \
264+ else \
265+ echo ' {"level":"info","msg":"skipping rust lint: no .rs files found","language":"rust"}' >&2 ; \
266+ fi ; \
267+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
268+ end_time=$$(date +%s%3N ) ; \
269+ duration=$$((end_time - start_time ) ); \
270+ echo " {\" target\" :\" lint\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
271+ exit $$ overall_exit; \
272+ fi ; \
273+ fi ; \
258274 end_time=$$(date +%s%3N ) ; \
259275 duration=$$((end_time - start_time ) ); \
260276 if [ $$ overall_exit -eq 0 ]; then \
@@ -298,6 +314,10 @@ _format: _check-config
298314 if [ -n " $( HAS_TERRAFORM) " ]; then \
299315 ran_languages=" $$ {ran_languages}\" terraform\" ," ; \
300316 terraform fmt -check -recursive || { overall_exit=1; failed_languages=" $$ {failed_languages}\" terraform\" ," ; }; \
317+ tg_files=$$(find . -name 'terragrunt.hcl' -not -path './.git/*' -not -path './.terraform/*' 2>/dev/null ) ; \
318+ if [ -n " $$ tg_files" ]; then \
319+ terragrunt hclfmt --terragrunt-check || { overall_exit=1; failed_languages=" $$ {failed_languages}\" terraform:terragrunt\" ," ; }; \
320+ fi ; \
301321 if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
302322 end_time=$$(date +%s%3N ) ; \
303323 duration=$$((end_time - start_time ) ); \
@@ -354,6 +374,21 @@ _format: _check-config
354374 exit $$ overall_exit; \
355375 fi ; \
356376 fi ; \
377+ if [ -n " $( HAS_RUST) " ]; then \
378+ ran_languages=" $$ {ran_languages}\" rust\" ," ; \
379+ rs_files=$$(find . -name '*.rs' -not -path './.git/*' -not -path './vendor/*' -not -path './target/*' 2>/dev/null ) ; \
380+ if [ -n " $$ rs_files" ]; then \
381+ cargo fmt --all -- --check || { overall_exit=1; failed_languages=" $$ {failed_languages}\" rust\" ," ; }; \
382+ else \
383+ echo ' {"level":"info","msg":"skipping rust format: no .rs files found","language":"rust"}' >&2 ; \
384+ fi ; \
385+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
386+ end_time=$$(date +%s%3N ) ; \
387+ duration=$$((end_time - start_time ) ); \
388+ echo " {\" target\" :\" format\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
389+ exit $$ overall_exit; \
390+ fi ; \
391+ fi ; \
357392 end_time=$$(date +%s%3N ) ; \
358393 duration=$$((end_time - start_time ) ); \
359394 if [ $$ overall_exit -eq 0 ]; then \
@@ -363,6 +398,124 @@ _format: _check-config
363398 fi ; \
364399 exit $$ overall_exit
365400
401+ # --- _fix: language-specific format fixing (in-place) ---
402+ _fix : _check-config
403+ @start_time=$$(date +%s%3N ) ; \
404+ overall_exit=0; \
405+ ran_languages=" " ; \
406+ failed_languages=" " ; \
407+ if [ -n " $( HAS_PYTHON) " ]; then \
408+ ran_languages=" $$ {ran_languages}\" python\" ," ; \
409+ ruff format . || { overall_exit=1; failed_languages=" $$ {failed_languages}\" python\" ," ; }; \
410+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
411+ end_time=$$(date +%s%3N ) ; \
412+ duration=$$((end_time - start_time ) ); \
413+ echo " {\" target\" :\" fix\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
414+ exit $$ overall_exit; \
415+ fi ; \
416+ fi ; \
417+ if [ -n " $( HAS_BASH) " ]; then \
418+ ran_languages=" $$ {ran_languages}\" bash\" ," ; \
419+ sh_files=$$(find . -name '*.sh' -not -path './.git/*' -not -path './vendor/*' -not -path './node_modules/*' 2>/dev/null ) ; \
420+ if [ -n " $$ sh_files" ]; then \
421+ echo " $$ sh_files" | xargs shfmt -w || { overall_exit=1; failed_languages=" $$ {failed_languages}\" bash\" ," ; }; \
422+ else \
423+ echo ' {"level":"info","msg":"skipping bash fix: no .sh files found","language":"bash"}' >&2 ; \
424+ fi ; \
425+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
426+ end_time=$$(date +%s%3N ) ; \
427+ duration=$$((end_time - start_time ) ); \
428+ echo " {\" target\" :\" fix\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
429+ exit $$ overall_exit; \
430+ fi ; \
431+ fi ; \
432+ if [ -n " $( HAS_TERRAFORM) " ]; then \
433+ ran_languages=" $$ {ran_languages}\" terraform\" ," ; \
434+ terraform fmt -recursive || { overall_exit=1; failed_languages=" $$ {failed_languages}\" terraform\" ," ; }; \
435+ tg_files=$$(find . -name 'terragrunt.hcl' -not -path './.git/*' -not -path './.terraform/*' 2>/dev/null ) ; \
436+ if [ -n " $$ tg_files" ]; then \
437+ terragrunt hclfmt || { overall_exit=1; failed_languages=" $$ {failed_languages}\" terraform:terragrunt\" ," ; }; \
438+ fi ; \
439+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
440+ end_time=$$(date +%s%3N ) ; \
441+ duration=$$((end_time - start_time ) ); \
442+ echo " {\" target\" :\" fix\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
443+ exit $$ overall_exit; \
444+ fi ; \
445+ fi ; \
446+ if [ -n " $( HAS_ANSIBLE) " ]; then \
447+ ran_languages=" $$ {ran_languages}\" ansible\" ," ; \
448+ echo ' {"target":"fix","language":"ansible","status":"skip","reason":"no formatter configured"}' >&2 ; \
449+ fi ; \
450+ if [ -n " $( HAS_RUBY) " ]; then \
451+ ran_languages=" $$ {ran_languages}\" ruby\" ," ; \
452+ rb_files=$$(find . -name '*.rb' -not -path './.git/*' -not -path './vendor/*' -not -path './node_modules/*' 2>/dev/null ) ; \
453+ if [ -n " $$ rb_files" ]; then \
454+ rubocop -a . || { overall_exit=1; failed_languages=" $$ {failed_languages}\" ruby\" ," ; }; \
455+ else \
456+ echo ' {"level":"info","msg":"skipping ruby fix: no .rb files found","language":"ruby"}' >&2 ; \
457+ fi ; \
458+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
459+ end_time=$$(date +%s%3N ) ; \
460+ duration=$$((end_time - start_time ) ); \
461+ echo " {\" target\" :\" fix\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
462+ exit $$ overall_exit; \
463+ fi ; \
464+ fi ; \
465+ if [ -n " $( HAS_GO) " ]; then \
466+ ran_languages=" $$ {ran_languages}\" go\" ," ; \
467+ go_files=$$(find . -name '*.go' -not -path './.git/*' -not -path './vendor/*' -not -path './node_modules/*' 2>/dev/null ) ; \
468+ if [ -n " $$ go_files" ]; then \
469+ gofumpt -w . || { overall_exit=1; failed_languages=" $$ {failed_languages}\" go\" ," ; }; \
470+ else \
471+ echo ' {"level":"info","msg":"skipping go fix: no .go files found","language":"go"}' >&2 ; \
472+ fi ; \
473+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
474+ end_time=$$(date +%s%3N ) ; \
475+ duration=$$((end_time - start_time ) ); \
476+ echo " {\" target\" :\" fix\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
477+ exit $$ overall_exit; \
478+ fi ; \
479+ fi ; \
480+ if [ -n " $( HAS_JAVASCRIPT) " ]; then \
481+ ran_languages=" $$ {ran_languages}\" javascript\" ," ; \
482+ js_files=$$(find . \( -name '*.js' -o -name '*.jsx' -o -name '*.ts' -o -name '*.tsx' -o -name '*.mjs' -o -name '*.cjs' \ ) -not -path ' ./.git/*' -not -path ' ./vendor/*' -not -path ' ./node_modules/*' -not -path ' ./dist/*' -not -path ' ./build/*' 2> /dev/null); \
483+ if [ -n " $$ js_files" ]; then \
484+ prettier --write . || { overall_exit=1; failed_languages=" $$ {failed_languages}\" javascript\" ," ; }; \
485+ else \
486+ echo ' {"level":"info","msg":"skipping javascript fix: no JS/TS files found","language":"javascript"}' >&2 ; \
487+ fi ; \
488+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
489+ end_time=$$(date +%s%3N ) ; \
490+ duration=$$((end_time - start_time ) ); \
491+ echo " {\" target\" :\" fix\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
492+ exit $$ overall_exit; \
493+ fi ; \
494+ fi ; \
495+ if [ -n " $( HAS_RUST) " ]; then \
496+ ran_languages=" $$ {ran_languages}\" rust\" ," ; \
497+ rs_files=$$(find . -name '*.rs' -not -path './.git/*' -not -path './vendor/*' -not -path './target/*' 2>/dev/null ) ; \
498+ if [ -n " $$ rs_files" ]; then \
499+ cargo fmt --all || { overall_exit=1; failed_languages=" $$ {failed_languages}\" rust\" ," ; }; \
500+ else \
501+ echo ' {"level":"info","msg":"skipping rust fix: no .rs files found","language":"rust"}' >&2 ; \
502+ fi ; \
503+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
504+ end_time=$$(date +%s%3N ) ; \
505+ duration=$$((end_time - start_time ) ); \
506+ echo " {\" target\" :\" fix\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
507+ exit $$ overall_exit; \
508+ fi ; \
509+ fi ; \
510+ end_time=$$(date +%s%3N ) ; \
511+ duration=$$((end_time - start_time ) ); \
512+ if [ $$ overall_exit -eq 0 ]; then \
513+ echo " {\" target\" :\" fix\" ,\" status\" :\" pass\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}]}" ; \
514+ else \
515+ echo " {\" target\" :\" fix\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
516+ fi ; \
517+ exit $$ overall_exit
518+
366519# --- _test: language-specific test runners ---
367520_test : _check-config
368521 @start_time=$$(date +%s%3N ) ; \
@@ -475,6 +628,22 @@ _test: _check-config
475628 exit $$ overall_exit; \
476629 fi ; \
477630 fi ; \
631+ if [ -n " $( HAS_RUST) " ]; then \
632+ rs_files=$$(find . -name '*.rs' -not -path './.git/*' -not -path './vendor/*' -not -path './target/*' 2>/dev/null ) ; \
633+ if [ -n " $$ rs_files" ] && [ -f " Cargo.toml" ]; then \
634+ ran_languages=" $$ {ran_languages}\" rust\" ," ; \
635+ cargo test --all-targets || { overall_exit=1; failed_languages=" $$ {failed_languages}\" rust\" ," ; }; \
636+ else \
637+ skipped_languages=" $$ {skipped_languages}\" rust\" ," ; \
638+ echo ' {"level":"info","msg":"skipping rust tests: no .rs files or Cargo.toml found","language":"rust"}' >&2 ; \
639+ fi ; \
640+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
641+ end_time=$$(date +%s%3N ) ; \
642+ duration=$$((end_time - start_time ) ); \
643+ echo " {\" target\" :\" test\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}],\" skipped\" :[$$ {skipped_languages%,}]}" ; \
644+ exit $$ overall_exit; \
645+ fi ; \
646+ fi ; \
478647 end_time=$$(date +%s%3N ) ; \
479648 duration=$$((end_time - start_time ) ); \
480649 if [ -z " $$ {ran_languages}" ] && [ -n " $$ {skipped_languages}" ]; then \
@@ -590,6 +759,32 @@ _security: _check-config
590759 exit $$ overall_exit; \
591760 fi ; \
592761 fi ; \
762+ if [ -n " $( HAS_RUST) " ]; then \
763+ if [ -f " Cargo.lock" ]; then \
764+ ran_languages=" $$ {ran_languages}\" rust\" ," ; \
765+ cargo audit || { overall_exit=1; failed_languages=" $$ {failed_languages}\" rust:cargo-audit\" ," ; }; \
766+ else \
767+ skipped_languages=" $$ {skipped_languages}\" rust:cargo-audit\" ," ; \
768+ echo ' {"level":"info","msg":"skipping cargo audit: no Cargo.lock found","language":"rust"}' >&2 ; \
769+ fi ; \
770+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
771+ end_time=$$(date +%s%3N ) ; \
772+ duration=$$((end_time - start_time ) ); \
773+ echo " {\" target\" :\" security\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
774+ exit $$ overall_exit; \
775+ fi ; \
776+ if [ -f " deny.toml" ]; then \
777+ cargo deny check || { overall_exit=1; failed_languages=" $$ {failed_languages}\" rust:cargo-deny\" ," ; }; \
778+ else \
779+ echo ' {"level":"info","msg":"skipping cargo deny: no deny.toml found","language":"rust"}' >&2 ; \
780+ fi ; \
781+ if [ " $( DEVRAIL_FAIL_FAST) " = " 1" ] && [ $$ overall_exit -ne 0 ]; then \
782+ end_time=$$(date +%s%3N ) ; \
783+ duration=$$((end_time - start_time ) ); \
784+ echo " {\" target\" :\" security\" ,\" status\" :\" fail\" ,\" duration_ms\" :$$ duration,\" languages\" :[$$ {ran_languages%,}],\" failed\" :[$$ {failed_languages%,}]}" ; \
785+ exit $$ overall_exit; \
786+ fi ; \
787+ fi ; \
593788 end_time=$$(date +%s%3N ) ; \
594789 duration=$$((end_time - start_time ) ); \
595790 if [ -z " $$ {ran_languages}" ] && [ -n " $$ {skipped_languages}" ]; then \
@@ -682,6 +877,7 @@ _docs: _check-config
682877 _tv tfsec " tfsec --version" ; \
683878 _tv checkov " checkov --version" ; \
684879 _tv terraform-docs " terraform-docs --version" ; \
880+ _tv terragrunt " terragrunt --version" ; \
685881 fi ; \
686882 if [ -n " $( HAS_ANSIBLE) " ]; then \
687883 _tv ansible-lint " ansible-lint --version" ; \
@@ -709,6 +905,14 @@ _docs: _check-config
709905 _tv tsc " tsc --version" ; \
710906 _tv vitest " vitest --version" ; \
711907 fi ; \
908+ if [ -n " $( HAS_RUST) " ]; then \
909+ _tv rustc " rustc --version" ; \
910+ _tv cargo " cargo --version" ; \
911+ _tv clippy " cargo clippy --version" ; \
912+ _tv rustfmt " rustfmt --version" ; \
913+ _tv cargo-audit " cargo audit --version" ; \
914+ _tv cargo-deny " cargo deny --version" ; \
915+ fi ; \
712916 _tv trivy " trivy --version" ; \
713917 _tv gitleaks " gitleaks version" ; \
714918 _tv git-cliff " git-cliff --version" ; \
@@ -915,6 +1119,45 @@ _init: _check-config
9151119 ' build/' \
9161120 ' coverage/' ; \
9171121 fi ; \
1122+ if [ -n " $( HAS_RUST) " ]; then \
1123+ scaffold clippy.toml \
1124+ ' # clippy.toml -- DevRail Rust clippy configuration' \
1125+ ' # See: https://doc.rust-lang.org/clippy/lint_configuration.html' \
1126+ ' too-many-arguments-threshold = 7' ; \
1127+ scaffold rustfmt.toml \
1128+ ' # rustfmt.toml -- DevRail Rust formatter configuration' \
1129+ ' edition = "2021"' \
1130+ ' max_width = 100' \
1131+ ' use_field_init_shorthand = true' \
1132+ ' use_try_shorthand = true' ; \
1133+ scaffold deny.toml \
1134+ ' # deny.toml -- DevRail cargo-deny configuration' \
1135+ ' # See: https://embarkstudios.github.io/cargo-deny/' \
1136+ ' ' \
1137+ ' [advisories]' \
1138+ ' vulnerability = "deny"' \
1139+ ' unmaintained = "warn"' \
1140+ ' yanked = "warn"' \
1141+ ' ' \
1142+ ' [licenses]' \
1143+ ' unlicensed = "deny"' \
1144+ ' allow = [' \
1145+ ' "MIT",' \
1146+ ' "Apache-2.0",' \
1147+ ' "BSD-2-Clause",' \
1148+ ' "BSD-3-Clause",' \
1149+ ' "ISC",' \
1150+ ' "Unicode-3.0",' \
1151+ ' "Unicode-DFS-2016",' \
1152+ ' ]' \
1153+ ' ' \
1154+ ' [bans]' \
1155+ ' multiple-versions = "warn"' \
1156+ ' ' \
1157+ ' [sources]' \
1158+ ' unknown-registry = "deny"' \
1159+ ' unknown-git = "warn"' ; \
1160+ fi ; \
9181161 echo " {\" target\" :\" init\" ,\" created\" :[$$ {created%,}],\" skipped\" :[$$ {skipped%,}]}"
9191162
9201163# --- _check: orchestrate all targets ---
0 commit comments