summaryrefslogtreecommitdiff
path: root/.github/workflows/linearize.sh
blob: f793ac6d3a865e7df8927233dff5dc60d83e7d26 (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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
#!/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=..

# The donated Japanese index accompanies the English one when present.
readmes=(README.md)
if [[ -f "$repo/README.ja.md" ]]; then
  readmes+=(README.ja.md)
fi

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=("${readmes[@]}" 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
  # Grammar documents now sit beside their chapter README.
  for doc in docs/*.md; do
    [[ -f "$doc" ]] || continue
    sed -Ei 's,\(\.\./README(\.ja)?\.md([#)]),(README\1.md\2,g' "$doc"
    sed -Ei 's,\(\.\./\.\./(chapter[0-9]+[a-z]?)/docs/,(../\1/,g' "$doc"
  done
  # Keep the READMEs and supporting notes together, including Japanese copies.
  mv *.md docs/
  mv docs chapter_docs
  if [[ -d appendix ]]; then
    mv appendix chapter_docs/
    sed -Ei 's,\(\.\./docs/,(../,g' chapter_docs/appendix/*.md
  fi
  rm pom.xml || :
  git add .

  # Add the shared files in the root, except for the indexes 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 the English/Japanese indexes, chapter READMEs, 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 "${readmes[@]}" pom.xml | xargs git checkout -q HEAD
  else
    # This is the first commit; setup shared files.
    cp "${readmes[@]/#/"$repo/"}" "$repo/pom.xml" .
    mkdir docs
    # Shared type-domain illustrations are referenced from chapter READMEs.
    cp "$repo"/docs/type-*.svg docs/
    git add docs/type-*.svg
    if [[ -f "$repo/docs/japanese-translation.md" ]]; then
      cp "$repo/docs/japanese-translation.md" docs/
      git add docs/japanese-translation.md
    fi
    # Remove links to chapters.
    sed -Ei 's,\[Chapter ([0-9]+[a-z]?)\]\(chapter0?\1/README\.md\),Chapter \1,' README.md
    if [[ -f README.ja.md ]]; then
      sed -Ei 's,\[([^]]+)\]\(chapter[0-9]+[a-z]?/README(\.ja)?\.md\),\1,g' README.ja.md
    fi
    # 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
  if [[ -f README.ja.md ]]; then
    translated=README.md
    if [[ -f "docs/$chapter/README.ja.md" ]]; then
      translated=README.ja.md
    fi
    sed -Ei "s,^\* 第${chapter_number}章:,* [第${chapter_number}章](docs/$chapter/$translated):," README.ja.md
  fi
  # Repair links for this chapter.
  for doc in "docs/$chapter"/README*.md; do
    sed -Ei 's,\bdocs/,,g' "$doc"
    sed -Ei 's,\(\.\./(isa|graph|print)/,(../../\1/,g' "$doc"
    sed -Ei 's,\(\.\./README(\.ja)?\.md([#)]),(../../README\1.md\2,g' "$doc"
    # Sources live at the root in a standalone chapter checkout.
    sed -Ei 's,\((\./)?src/,(../../src/,g' "$doc"
  done
  git add "${readmes[@]}" "docs/$chapter" 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!'