diff --git a/README.md b/README.md index 70b66da..512f10c 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,10 @@ Build the static site using npm run build ``` +### Updating the redirects + +Run ` ./generate_redirects.sh ./cards` from the root of this project to update the `_redirects` fils. This will enable netlify to redirect the "naked" URLs to the new nested structure + ## Deploying to essentials.xebia.com TBD diff --git a/_redirects b/_redirects new file mode 100644 index 0000000..a4a9e86 --- /dev/null +++ b/_redirects @@ -0,0 +1,79 @@ +# Auto-generated by generate_redirects.sh on 2025-08-18T12:21:10Z +# Root: ./cards +# Status: 301 +# Slugify FROM: 0 +/index / 301 +/no-blame-no-mercy /base/collaboration/no-blame-no-mercy 301 +/no-broken-windows /base/collaboration/no-broken-windows 301 +/pair-programming /base/collaboration/pair-programming 301 +/have-fun /base/collaboration/have-fun 301 +/team-rhythm /base/collaboration/team-rhythm 301 +/build-it-run-it /base/collaboration/build-it-run-it 301 +/eliminate-waste /base/collaboration/eliminate-waste 301 +/hurt-often /base/collaboration/hurt-often 301 +/brutal-transparency /base/collaboration/brutal-transparency 301 +/no-broken-builds /base/collaboration/no-broken-builds 301 +/one-feature-at-a-time /base/collaboration/one-feature-at-a-time 301 +/alone-time /base/collaboration/alone-time 301 +/done /base/collaboration/done 301 +/the-zone /base/collaboration/the-zone 301 +/definition-of-ready /base/collaboration/definition-of-ready 301 +/focus-on-flow /base/collaboration/focus-on-flow 301 +/make-it-visible /base/collaboration/make-it-visible 301 +/timebox /base/collaboration/timebox 301 +/code-review /base/collaboration/code-review 301 +/what-you-measure /base/collaboration/what-you-measure 301 +/team-member-equality /base/collaboration/team-member-equality 301 +/done-is-live /base/collaboration/done-is-live 301 +/small-increments /base/craftsmanship/small-increments 301 +/dry-principle /base/craftsmanship/dry-principle 301 +/no-multitasking /base/craftsmanship/no-multitasking 301 +/curiosity /base/craftsmanship/curiosity 301 +/comment-with-care /base/craftsmanship/comment-with-care 301 +/time-for-tech-debt /base/craftsmanship/time-for-tech-debt 301 +/dare-to-say-no /base/craftsmanship/dare-to-say-no 301 +/no-test-no-bugfix /base/craftsmanship/no-test-no-bugfix 301 +/two-minute-rule /base/craftsmanship/two-minute-rule 301 +/improve-continuously /base/craftsmanship/improve-continuously 301 +/learn-a-new-language /base/craftsmanship/learn-a-new-language 301 +/readable-code /base/craftsmanship/readable-code 301 +/one-change-at-a-time /base/craftsmanship/one-change-at-a-time 301 +/no-museum /base/craftsmanship/no-museum 301 +/genchi-genbutsu /base/craftsmanship/genchi-genbutsu 301 +/make-it-work-right-fast /base/craftsmanship/make-it-work-right-fast 301 +/rich-communication /base/craftsmanship/rich-communication 301 +/context-over-habit /base/craftsmanship/context-over-habit 301 +/diagnose-before-cure /base/craftsmanship/diagnose-before-cure 301 +/clean-build /base/craftsmanship/clean-build 301 +/three-strikes /base/craftsmanship/three-strikes 301 +/master-your-tools /base/craftsmanship/master-your-tools 301 +/tests-are-specs /base/testing/tests-are-specs 301 +/test-everything /base/testing/test-everything 301 +/independent-tests /base/testing/independent-tests 301 +/run-tests-automatically /base/testing/run-tests-automatically 301 +/acceptance-criteria /base/testing/acceptance-criteria 301 +/testing-is-shared-responsibility /base/testing/testing-is-shared-responsibility 301 +/exploratory-testing /base/testing/exploratory-testing 301 +/tdd-shapes-design /base/testing/tdd-shapes-design 301 +/fail-fast /base/testing/fail-fast 301 +/clean-logs /base/testing/clean-logs 301 +/test-code-one /base/testing/test-code-one 301 +/kiss /base/realisation/kiss 301 +/integrate-early /base/realisation/integrate-early 301 +/automate-everything /base/realisation/automate-everything 301 +/secure-development /base/realisation/secure-development 301 +/poutsma-principle /base/realisation/poutsma-principle 301 +/shared-design-understanding /base/realisation/shared-design-understanding 301 +/thirty-minute-methods /base/realisation/thirty-minute-methods 301 +/maximize-cohesion-minimize-coupling /base/realisation/maximize-cohesion-minimize-coupling 301 +/apis-are-forever /base/realisation/apis-are-forever 301 +/separation-of-concerns /base/realisation/separation-of-concerns 301 +/thread-safe /base/realisation/thread-safe 301 +/non-functionals /base/realisation/non-functionals 301 +/boy-scout-rule /base/realisation/boy-scout-rule 301 +/fallacies-distributed-computing /base/realisation/fallacies-distributed-computing 301 +/composition-over-inheritance /base/realisation/composition-over-inheritance 301 +/hands-off-machine /base/realisation/hands-off-machine 301 +/assertions /base/realisation/assertions 301 +/focused-interfaces /base/realisation/focused-interfaces 301 +/no-anemic-domain-model /base/realisation/no-anemic-domain-model 301 diff --git a/cards/alone-time.md b/cards/base/collaboration/alone-time.md similarity index 100% rename from cards/alone-time.md rename to cards/base/collaboration/alone-time.md diff --git a/cards/brutal-transparency.md b/cards/base/collaboration/brutal-transparency.md similarity index 100% rename from cards/brutal-transparency.md rename to cards/base/collaboration/brutal-transparency.md diff --git a/cards/build-it-run-it.md b/cards/base/collaboration/build-it-run-it.md similarity index 100% rename from cards/build-it-run-it.md rename to cards/base/collaboration/build-it-run-it.md diff --git a/cards/code-review.md b/cards/base/collaboration/code-review.md similarity index 100% rename from cards/code-review.md rename to cards/base/collaboration/code-review.md diff --git a/cards/definition-of-ready.md b/cards/base/collaboration/definition-of-ready.md similarity index 100% rename from cards/definition-of-ready.md rename to cards/base/collaboration/definition-of-ready.md diff --git a/cards/done-is-live.md b/cards/base/collaboration/done-is-live.md similarity index 100% rename from cards/done-is-live.md rename to cards/base/collaboration/done-is-live.md diff --git a/cards/done.md b/cards/base/collaboration/done.md similarity index 100% rename from cards/done.md rename to cards/base/collaboration/done.md diff --git a/cards/eliminate-waste.md b/cards/base/collaboration/eliminate-waste.md similarity index 100% rename from cards/eliminate-waste.md rename to cards/base/collaboration/eliminate-waste.md diff --git a/cards/focus-on-flow.md b/cards/base/collaboration/focus-on-flow.md similarity index 100% rename from cards/focus-on-flow.md rename to cards/base/collaboration/focus-on-flow.md diff --git a/cards/have-fun.md b/cards/base/collaboration/have-fun.md similarity index 100% rename from cards/have-fun.md rename to cards/base/collaboration/have-fun.md diff --git a/cards/hurt-often.md b/cards/base/collaboration/hurt-often.md similarity index 100% rename from cards/hurt-often.md rename to cards/base/collaboration/hurt-often.md diff --git a/cards/make-it-visible.md b/cards/base/collaboration/make-it-visible.md similarity index 100% rename from cards/make-it-visible.md rename to cards/base/collaboration/make-it-visible.md diff --git a/cards/no-blame-no-mercy.md b/cards/base/collaboration/no-blame-no-mercy.md similarity index 100% rename from cards/no-blame-no-mercy.md rename to cards/base/collaboration/no-blame-no-mercy.md diff --git a/cards/no-broken-builds.md b/cards/base/collaboration/no-broken-builds.md similarity index 100% rename from cards/no-broken-builds.md rename to cards/base/collaboration/no-broken-builds.md diff --git a/cards/no-broken-windows.md b/cards/base/collaboration/no-broken-windows.md similarity index 100% rename from cards/no-broken-windows.md rename to cards/base/collaboration/no-broken-windows.md diff --git a/cards/one-feature-at-a-time.md b/cards/base/collaboration/one-feature-at-a-time.md similarity index 100% rename from cards/one-feature-at-a-time.md rename to cards/base/collaboration/one-feature-at-a-time.md diff --git a/cards/pair-programming.md b/cards/base/collaboration/pair-programming.md similarity index 100% rename from cards/pair-programming.md rename to cards/base/collaboration/pair-programming.md diff --git a/cards/team-member-equality.md b/cards/base/collaboration/team-member-equality.md similarity index 100% rename from cards/team-member-equality.md rename to cards/base/collaboration/team-member-equality.md diff --git a/cards/team-rhythm.md b/cards/base/collaboration/team-rhythm.md similarity index 100% rename from cards/team-rhythm.md rename to cards/base/collaboration/team-rhythm.md diff --git a/cards/the-zone.md b/cards/base/collaboration/the-zone.md similarity index 100% rename from cards/the-zone.md rename to cards/base/collaboration/the-zone.md diff --git a/cards/timebox.md b/cards/base/collaboration/timebox.md similarity index 100% rename from cards/timebox.md rename to cards/base/collaboration/timebox.md diff --git a/cards/what-you-measure.md b/cards/base/collaboration/what-you-measure.md similarity index 100% rename from cards/what-you-measure.md rename to cards/base/collaboration/what-you-measure.md diff --git a/cards/clean-build.md b/cards/base/craftsmanship/clean-build.md similarity index 100% rename from cards/clean-build.md rename to cards/base/craftsmanship/clean-build.md diff --git a/cards/comment-with-care.md b/cards/base/craftsmanship/comment-with-care.md similarity index 100% rename from cards/comment-with-care.md rename to cards/base/craftsmanship/comment-with-care.md diff --git a/cards/context-over-habit.md b/cards/base/craftsmanship/context-over-habit.md similarity index 100% rename from cards/context-over-habit.md rename to cards/base/craftsmanship/context-over-habit.md diff --git a/cards/curiosity.md b/cards/base/craftsmanship/curiosity.md similarity index 100% rename from cards/curiosity.md rename to cards/base/craftsmanship/curiosity.md diff --git a/cards/dare-to-say-no.md b/cards/base/craftsmanship/dare-to-say-no.md similarity index 100% rename from cards/dare-to-say-no.md rename to cards/base/craftsmanship/dare-to-say-no.md diff --git a/cards/diagnose-before-cure.md b/cards/base/craftsmanship/diagnose-before-cure.md similarity index 100% rename from cards/diagnose-before-cure.md rename to cards/base/craftsmanship/diagnose-before-cure.md diff --git a/cards/dry-principle.md b/cards/base/craftsmanship/dry-principle.md similarity index 100% rename from cards/dry-principle.md rename to cards/base/craftsmanship/dry-principle.md diff --git a/cards/genchi-genbutsu.md b/cards/base/craftsmanship/genchi-genbutsu.md similarity index 100% rename from cards/genchi-genbutsu.md rename to cards/base/craftsmanship/genchi-genbutsu.md diff --git a/cards/improve-continuously.md b/cards/base/craftsmanship/improve-continuously.md similarity index 100% rename from cards/improve-continuously.md rename to cards/base/craftsmanship/improve-continuously.md diff --git a/cards/learn-a-new-language.md b/cards/base/craftsmanship/learn-a-new-language.md similarity index 100% rename from cards/learn-a-new-language.md rename to cards/base/craftsmanship/learn-a-new-language.md diff --git a/cards/make-it-work-right-fast.md b/cards/base/craftsmanship/make-it-work-right-fast.md similarity index 100% rename from cards/make-it-work-right-fast.md rename to cards/base/craftsmanship/make-it-work-right-fast.md diff --git a/cards/master-your-tools.md b/cards/base/craftsmanship/master-your-tools.md similarity index 100% rename from cards/master-your-tools.md rename to cards/base/craftsmanship/master-your-tools.md diff --git a/cards/no-multitasking.md b/cards/base/craftsmanship/no-multitasking.md similarity index 100% rename from cards/no-multitasking.md rename to cards/base/craftsmanship/no-multitasking.md diff --git a/cards/no-museum.md b/cards/base/craftsmanship/no-museum.md similarity index 100% rename from cards/no-museum.md rename to cards/base/craftsmanship/no-museum.md diff --git a/cards/no-test-no-bugfix.md b/cards/base/craftsmanship/no-test-no-bugfix.md similarity index 100% rename from cards/no-test-no-bugfix.md rename to cards/base/craftsmanship/no-test-no-bugfix.md diff --git a/cards/one-change-at-a-time.md b/cards/base/craftsmanship/one-change-at-a-time.md similarity index 100% rename from cards/one-change-at-a-time.md rename to cards/base/craftsmanship/one-change-at-a-time.md diff --git a/cards/readable-code.md b/cards/base/craftsmanship/readable-code.md similarity index 100% rename from cards/readable-code.md rename to cards/base/craftsmanship/readable-code.md diff --git a/cards/rich-communication.md b/cards/base/craftsmanship/rich-communication.md similarity index 100% rename from cards/rich-communication.md rename to cards/base/craftsmanship/rich-communication.md diff --git a/cards/small-increments.md b/cards/base/craftsmanship/small-increments.md similarity index 100% rename from cards/small-increments.md rename to cards/base/craftsmanship/small-increments.md diff --git a/cards/three-strikes.md b/cards/base/craftsmanship/three-strikes.md similarity index 100% rename from cards/three-strikes.md rename to cards/base/craftsmanship/three-strikes.md diff --git a/cards/time-for-tech-debt.md b/cards/base/craftsmanship/time-for-tech-debt.md similarity index 100% rename from cards/time-for-tech-debt.md rename to cards/base/craftsmanship/time-for-tech-debt.md diff --git a/cards/two-minute-rule.md b/cards/base/craftsmanship/two-minute-rule.md similarity index 100% rename from cards/two-minute-rule.md rename to cards/base/craftsmanship/two-minute-rule.md diff --git a/cards/apis-are-forever.md b/cards/base/realisation/apis-are-forever.md similarity index 100% rename from cards/apis-are-forever.md rename to cards/base/realisation/apis-are-forever.md diff --git a/cards/assertions.md b/cards/base/realisation/assertions.md similarity index 100% rename from cards/assertions.md rename to cards/base/realisation/assertions.md diff --git a/cards/automate-everything.md b/cards/base/realisation/automate-everything.md similarity index 100% rename from cards/automate-everything.md rename to cards/base/realisation/automate-everything.md diff --git a/cards/boy-scout-rule.md b/cards/base/realisation/boy-scout-rule.md similarity index 100% rename from cards/boy-scout-rule.md rename to cards/base/realisation/boy-scout-rule.md diff --git a/cards/composition-over-inheritance.md b/cards/base/realisation/composition-over-inheritance.md similarity index 100% rename from cards/composition-over-inheritance.md rename to cards/base/realisation/composition-over-inheritance.md diff --git a/cards/fallacies-distributed-computing.md b/cards/base/realisation/fallacies-distributed-computing.md similarity index 100% rename from cards/fallacies-distributed-computing.md rename to cards/base/realisation/fallacies-distributed-computing.md diff --git a/cards/focused-interfaces.md b/cards/base/realisation/focused-interfaces.md similarity index 100% rename from cards/focused-interfaces.md rename to cards/base/realisation/focused-interfaces.md diff --git a/cards/hands-off-machine.md b/cards/base/realisation/hands-off-machine.md similarity index 100% rename from cards/hands-off-machine.md rename to cards/base/realisation/hands-off-machine.md diff --git a/cards/integrate-early.md b/cards/base/realisation/integrate-early.md similarity index 100% rename from cards/integrate-early.md rename to cards/base/realisation/integrate-early.md diff --git a/cards/kiss.md b/cards/base/realisation/kiss.md similarity index 100% rename from cards/kiss.md rename to cards/base/realisation/kiss.md diff --git a/cards/maximize-cohesion-minimize-coupling.md b/cards/base/realisation/maximize-cohesion-minimize-coupling.md similarity index 100% rename from cards/maximize-cohesion-minimize-coupling.md rename to cards/base/realisation/maximize-cohesion-minimize-coupling.md diff --git a/cards/no-anemic-domain-model.md b/cards/base/realisation/no-anemic-domain-model.md similarity index 100% rename from cards/no-anemic-domain-model.md rename to cards/base/realisation/no-anemic-domain-model.md diff --git a/cards/non-functionals.md b/cards/base/realisation/non-functionals.md similarity index 100% rename from cards/non-functionals.md rename to cards/base/realisation/non-functionals.md diff --git a/cards/poutsma-principle.md b/cards/base/realisation/poutsma-principle.md similarity index 100% rename from cards/poutsma-principle.md rename to cards/base/realisation/poutsma-principle.md diff --git a/cards/secure-development.md b/cards/base/realisation/secure-development.md similarity index 100% rename from cards/secure-development.md rename to cards/base/realisation/secure-development.md diff --git a/cards/separation-of-concerns.md b/cards/base/realisation/separation-of-concerns.md similarity index 100% rename from cards/separation-of-concerns.md rename to cards/base/realisation/separation-of-concerns.md diff --git a/cards/shared-design-understanding.md b/cards/base/realisation/shared-design-understanding.md similarity index 100% rename from cards/shared-design-understanding.md rename to cards/base/realisation/shared-design-understanding.md diff --git a/cards/thirty-minute-methods.md b/cards/base/realisation/thirty-minute-methods.md similarity index 100% rename from cards/thirty-minute-methods.md rename to cards/base/realisation/thirty-minute-methods.md diff --git a/cards/thread-safe.md b/cards/base/realisation/thread-safe.md similarity index 100% rename from cards/thread-safe.md rename to cards/base/realisation/thread-safe.md diff --git a/cards/acceptance-criteria.md b/cards/base/testing/acceptance-criteria.md similarity index 100% rename from cards/acceptance-criteria.md rename to cards/base/testing/acceptance-criteria.md diff --git a/cards/clean-logs.md b/cards/base/testing/clean-logs.md similarity index 100% rename from cards/clean-logs.md rename to cards/base/testing/clean-logs.md diff --git a/cards/exploratory-testing.md b/cards/base/testing/exploratory-testing.md similarity index 100% rename from cards/exploratory-testing.md rename to cards/base/testing/exploratory-testing.md diff --git a/cards/fail-fast.md b/cards/base/testing/fail-fast.md similarity index 100% rename from cards/fail-fast.md rename to cards/base/testing/fail-fast.md diff --git a/cards/independent-tests.md b/cards/base/testing/independent-tests.md similarity index 100% rename from cards/independent-tests.md rename to cards/base/testing/independent-tests.md diff --git a/cards/run-tests-automatically.md b/cards/base/testing/run-tests-automatically.md similarity index 100% rename from cards/run-tests-automatically.md rename to cards/base/testing/run-tests-automatically.md diff --git a/cards/tdd-shapes-design.md b/cards/base/testing/tdd-shapes-design.md similarity index 100% rename from cards/tdd-shapes-design.md rename to cards/base/testing/tdd-shapes-design.md diff --git a/cards/test-code-one.md b/cards/base/testing/test-code-one.md similarity index 100% rename from cards/test-code-one.md rename to cards/base/testing/test-code-one.md diff --git a/cards/test-everything.md b/cards/base/testing/test-everything.md similarity index 100% rename from cards/test-everything.md rename to cards/base/testing/test-everything.md diff --git a/cards/testing-is-shared-responsibility.md b/cards/base/testing/testing-is-shared-responsibility.md similarity index 100% rename from cards/testing-is-shared-responsibility.md rename to cards/base/testing/testing-is-shared-responsibility.md diff --git a/cards/tests-are-specs.md b/cards/base/testing/tests-are-specs.md similarity index 100% rename from cards/tests-are-specs.md rename to cards/base/testing/tests-are-specs.md diff --git a/generate_redirects.sh b/generate_redirects.sh new file mode 100755 index 0000000..4e247df --- /dev/null +++ b/generate_redirects.sh @@ -0,0 +1,127 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Generate a Netlify _redirects file mapping each markdown filename (no extension) +# to its nested path (no extension). +# +# Usage: +# ./generate_redirects.sh [root_dir] [output_file] +# +# Env vars: +# STATUS_CODE HTTP status to use (default: 301) +# SLUGIFY If "1", slugify the FROM path (lowercase, non-alnum -> "-") (default: 0) +# WARN_DUPLICATES If "1", warn when multiple files share the same FROM (default: 1) +# +# Examples: +# ./generate_redirects.sh content ./_redirects +# SLUGIFY=1 STATUS_CODE=302 ./generate_redirects.sh + +ROOT="${1:-.}" +OUT="${2:-_redirects}" +STATUS_CODE="${STATUS_CODE:-301}" +SLUGIFY="${SLUGIFY:-0}" +WARN_DUPLICATES="${WARN_DUPLICATES:-1}" + +if [ ! -d "$ROOT" ]; then + echo "Error: root directory \"$ROOT\" not found." >&2 + exit 1 +fi + +tmp="$(mktemp)" +trap 'rm -f "$tmp"' EXIT + +slugify() { + # lowercase, replace non-alnum with '-', trim leading/trailing '-' + # Note: uses POSIX tools + printf '%s' "$1" \ + | tr '[:upper:]' '[:lower:]' \ + | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//' +} + +normalize_path() { + # remove duplicate slashes, resolve "./" segments in a simple way + # keep leading slash + # input: path starting with "/" + printf '%s' "$1" \ + | sed -E 's#//+#/#g; s#/\./#/#g; s#^/\.?/?#/#' +} + +# Write header +{ + echo "# Auto-generated by generate_redirects.sh on $(date -u +'%Y-%m-%dT%H:%M:%SZ')" + echo "# Root: $ROOT" + echo "# Status: $STATUS_CODE" + echo "# Slugify FROM: $SLUGIFY" +} > "$OUT" + +# Collect raw rules into tmp as tab-separated: FROM TO STATUS +# - FROM: "/" (possibly slugified) +# - TO: "//" (index.md -> "/" ; root index.md -> "/") +# Walk the tree safely with NUL delimiters +find "$ROOT" -type f \( -iname '*.md' -o -iname '*.mdx' \) -print0 \ +| while IFS= read -r -d '' f; do + rel="${f#"$ROOT"/}" + base="$(basename "$f")" + name="${base%.*}" # filename without extension + dir="$(dirname "$rel")" + + from_name="$name" + if [ "$SLUGIFY" = "1" ]; then + from_name="$(slugify "$from_name")" + fi + + from="/$from_name" + + # Build TO path + if [ "$name" = "index" ]; then + if [ "$dir" = "." ]; then + to="/" + else + to="/$dir" + fi + else + if [ "$dir" = "." ]; then + to="/$name" + else + to="/$dir/$name" + fi + fi + + from="$(normalize_path "$from")" + to="$(normalize_path "$to")" + + # Skip empty FROM (possible if slugify produced empty) + if [ -z "$from" ] || [ "$from" = "/" ]; then + # Still allow "/index" if name was index and SLUGIFY removed it? We skip to be safe. + continue + fi + + # Write tab-separated for robust processing + printf "%s\t%s\t%s\n" "$from" "$to" "$STATUS_CODE" >> "$tmp" +done + +# Deduplicate by FROM (keep first); optionally warn about duplicates +awk -F '\t' -v OFS='\t' -v warn="$WARN_DUPLICATES" ' + { + from=$1 + if (!(from in seen)) { + seen[from]=NR + lines[NR]=$0 + order[NR]=from + } else if (warn) { + dups[from]++ + } + } + END { + for (i=1; i<=NR; i++) { + if (i in lines) print lines[i] + } + if (warn) { + for (k in dups) { + printf "WARNING: duplicate source \"%s\". Only the first occurrence is kept.\n", k > "/dev/stderr" + } + } + } +' "$tmp" >> "$OUT" + +echo "Wrote $(wc -l < "$OUT") lines to $OUT" \ No newline at end of file