summaryrefslogtreecommitdiff
path: root/.github/workflows/linearize.sh
blob: ef079bb89f4922a2a6f2e329fcb00ed6109c1054 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env bash
set -eEuo pipefail

sort_authors() {
  # Unify author aliases.
  sed -e 's/^dibyendumajumdar <mobile@majumdar\.org\.uk>$/Dibyendu Majumdar <mobile@majumdar.org.uk>/g' \
      -e 's/^Dibyendu Majumdar <dibyendumajumdar@users\.noreply\.github\.com>$/Dibyendu Majumdar <mobile@majumdar.org.uk>/g' |
  # Rank by number of commits.
  sort | uniq -c | sort -nr | sed -E 's/^ *[0-9]+ //'
}

IFS=$'\n'

mkdir linear
cd linear
git init -q -b main

repo=..

last_committer_date='0 +0000'

git -C "$repo" ls-tree HEAD --name-only | grep '^chapter' |
while read -r chapter; do
  echo "Processing $chapter"

  chapter_number="$(sed 's/^chapter0*//' <<< "$chapter")"

  if [[ ! -f "$repo/$chapter/README.md" ]]; then
    echo "Missing $chapter/READMD.md" >&2
    echo "# Chapter $chapter_number" > "$repo/$chapter/README.md"
  fi
  chapter_title="$(grep '^#' "$repo/$chapter/README.md" | head -n1 | sed 's/^#* //')"

  # Extract the authors for this chapter from commit authors and the
  # Co-authored-by trailer.
  authors="$(git -C "$repo" log --format='%an <%ae>' --no-merges "$chapter")"
  co_authors="$(git -C "$repo" log --format=%B --no-merges "$chapter" |
    tr -d $'\r' | sed -n 's/^ *Co-authored-by: //p')"
  first_author="$(sort_authors <<< "$authors" | head -n1)"
  all_authors="$(sort_authors <<< "$authors"$'\n'"$co_authors")"

  message="$chapter_title"$'\n\n'
  while read -r author; do
    if [[ "$author" != "$first_author" ]]; then
      message+="Co-authored-by: $author"$'\n'
    fi
  done <<< "$all_authors"

  # Get the first and last dates this chapter was modified.
  root_files=(README.md LICENSE pom.xml .gitignore .dir-locals.el transpile-tests graph print isa build-support)
  author_root_files=()
  if [[ $chapter = chapter01 ]]; then
    # Only include the commit date of the root files for chapter01.
    author_root_files+=("${root_files[@]}")
  fi
  author_date="$(git -C "$repo" log --format=%ad --date=raw "$chapter" "${author_root_files[@]}" | tail -n1)"
  committer_date="$(git -C "$repo" log --format=%ad --date=raw -1 "$chapter" "${root_files[@]}")"
  if [[ $committer_date < $last_committer_date ]]; then
    committer_date="$last_committer_date"
  else
    last_committer_date="$committer_date"
  fi

  # Add the files for the chapter.
  git rm -qr --ignore-unmatch .
  cp -R "$repo/$chapter/." .
  mkdir -p docs
  mv README.md docs/
  mv docs chapter_docs
  rm pom.xml || :
  git add .

  # Add the shared files in the root, except for README.md and pom.xml.
  root_files=(LICENSE .gitignore .dir-locals.el transpile-tests graph print isa build-support)
  cp -R "${root_files[@]/#/"$repo/"}" .
  git add "${root_files[@]}"

  # Fix README.md, docs/chapter??/README.md, and pom.xml.
  if git rev-parse HEAD >/dev/null 2>/dev/null; then
    # Restore the documentation and pom.xml from the previous chapter.
    git diff --staged --diff-filter=D --name-only -- docs README.md pom.xml | xargs git checkout -q HEAD
  else
    # This is the first commit; setup shared files.
    cp "$repo"/{README.md,pom.xml} .
    mkdir docs
    # Remove links to chapters.
    sed -Ei 's,\[Chapter ([0-9]+[a-z]?)\]\(chapter0?\1/README\.md\),Chapter \1,' README.md
    # Change to a JAR and delete the modules, for a single-project structure.
    sed -i -e 's,<packaging>pom</packaging>,<packaging>jar</packaging>,' \
           -e '/<modules>/,/^$/d' pom.xml
  fi
  # ISA sources enter the standalone Maven build with instruction encoding.
  if [[ "$chapter_number" == 21 ]]; then
    sed -i '/<source>${simple.print.directory}\/src\/main\/java<\/source>/a\                                <source>${project.basedir}/isa/src/main/java</source>' pom.xml
    sed -i '/<\/executions>/i\                    <execution>\
                        <id>shared-isa-test-sources</id>\
                        <phase>generate-test-sources</phase>\
                        <goals><goal>add-test-source</goal></goals>\
                        <configuration><sources>\
                            <source>${project.basedir}/isa/src/test-support/java</source>\
                        </sources></configuration>\
                    </execution>' pom.xml
  fi
  git mv chapter_docs "docs/$chapter"
  # Restore the link to this chapter.
  sed -Ei "s,^\* Chapter $chapter_number: ,* [Chapter $chapter_number](docs/$chapter/README.md): ," README.md
  # Repair links for this chapter.
  sed -Ei 's,\bdocs/,,' "docs/$chapter/README.md"
  sed -Ei 's,\(\.\./isa/,(../../isa/,g' "docs/$chapter/README.md"
  git add README.md "docs/$chapter/README.md" pom.xml

  # Create a commit for the chapter, using the metadata.
  if [[ ! "$first_author" =~ ^(.*)\ \<(.*)\>$ ]]; then
    echo "First author does not match pattern" >&2
    exit 1
  fi
  name="${BASH_REMATCH[1]}"
  email="${BASH_REMATCH[2]}"
  GIT_AUTHOR_NAME="$name" GIT_AUTHOR_EMAIL="$email" GIT_AUTHOR_DATE="$author_date" \
  GIT_COMMITTER_NAME="$name" GIT_COMMITTER_EMAIL="$email" GIT_COMMITTER_DATE="$committer_date" \
  git commit -q -m "$message"

  # Tag the chapter, so it can be stably linked.
  git tag "linear-$chapter"
done

echo 'Done!'