diff --git a/.gitignore b/.gitignore
index 378eac25d311703f3f2cd456d8036da525cd0366..1b763b1bae0d0061ad60e8847714df98e68f7eac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1 @@
-build
+CHANGELOG.md
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..97170830a7addee1190da3b8cb6f57e48bd2e94a
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,3 @@
+include:
+- project: kartenaale/pack-ci
+  file: '.gitlab-ci.yml'
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 65f25514b1a11bac826356da3395d32a0a3f7ed3..0000000000000000000000000000000000000000
--- a/Makefile
+++ /dev/null
@@ -1,29 +0,0 @@
-export PACK_TITLE := Sinologie Anki Pack, Teil 1
-export RELEASE_DIR_STEM := sinology-1
-export VERSION := $(file < version.txt)
-export CONTENT_DIR := $(PWD)/content
-export DOWNLOAD_PREFIX := build/
-export GITLAB_REPO := kartenaale/sinology-1
-
-.PHONY: all
-all: build
-# git commands in submake use the parent repo to generate the changelog, not
-# the build repo
-	GIT_DIR=$(PWD)/.git $(MAKE) -C build apkg announce changelog-last-version
-
-.PHONY: mostlyclean
-mostlyclean:
-	test -d build && $(MAKE) -C build mostlyclean || true
-
-.PHONY: clean
-clean:
-	test -d build && $(MAKE) -C build clean || true
-
-.PHONY: deepclean
-deepclean:
-	rm -rf build
-
-build: build.git build.sha
-	-rm -rf build
-	git clone $(file < build.git) build
-	cd build && git reset --hard $(file < build.sha)
diff --git a/build.git b/build.git
deleted file mode 100644
index f50cc7d8f6aeaf315989290f4234225eced23919..0000000000000000000000000000000000000000
--- a/build.git
+++ /dev/null
@@ -1 +0,0 @@
-git@gitlab.phaidra.org:kartenaale/sinologie-anki-pack.git
diff --git a/build.sha b/build.sha
deleted file mode 100644
index c5f2adb33bb2bd11dfc97eced5564ab6e541138d..0000000000000000000000000000000000000000
--- a/build.sha
+++ /dev/null
@@ -1 +0,0 @@
-7d3d4e6e1ba6b8c3b8bc7e4e34d3c838d3153e37
diff --git a/cliff.toml b/cliff.toml
new file mode 100644
index 0000000000000000000000000000000000000000..649f6cd00bf67775bd16745faae586f75b1a716e
--- /dev/null
+++ b/cliff.toml
@@ -0,0 +1,83 @@
+# git-cliff ~ default configuration file
+# https://git-cliff.org/docs/configuration
+#
+# Lines starting with "#" are comments.
+# Configuration options are organized into tables and keys.
+# See documentation for more information on available options.
+
+[changelog]
+# template for the changelog header
+header = """
+## Changelog\n
+"""
+# template for the changelog body
+# https://keats.github.io/tera/docs/#introduction
+body = """
+{% if version %}\
+    ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
+{% else %}\
+    ## [unreleased]
+{% endif %}\
+{% for group, commits in commits | group_by(attribute="group") %}
+    ### {{ group | striptags | trim | upper_first }}
+    {% for commit in commits %}
+        - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\
+            {% if commit.breaking %}[**breaking**] {% endif %}\
+            {{ commit.message | upper_first }}\
+    {% endfor %}
+{% endfor %}\n
+"""
+# template for the changelog footer
+footer = """
+<!-- generated by git-cliff -->
+"""
+# remove the leading and trailing s
+trim = true
+# postprocessors
+postprocessors = [
+  # { pattern = '<REPO>', replace = "https://github.com/orhun/git-cliff" }, # replace repository URL
+]
+
+[git]
+# parse the commits based on https://www.conventionalcommits.org
+conventional_commits = true
+# filter out the commits that are not conventional
+filter_unconventional = true
+# process each line of a commit as an individual commit
+split_commits = false
+# regex for preprocessing the commit messages
+commit_preprocessors = [
+  # Replace issue numbers
+  #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](<REPO>/issues/${2}))"},
+  # Check spelling of the commit with https://github.com/crate-ci/typos
+  # If the spelling is incorrect, it will be automatically fixed.
+  #{ pattern = '.*', replace_command = 'typos --write-changes -' },
+]
+# regex for parsing and grouping commits
+commit_parsers = [
+  { message = "^feat", group = "<!-- 0 -->๐Ÿš€ Features" },
+  { message = "^fix", group = "<!-- 1 -->๐Ÿ› Bug Fixes" },
+  { message = "^doc", group = "<!-- 3 -->๐Ÿ“š Documentation" },
+  { message = "^perf", group = "<!-- 4 -->โšก Performance" },
+  { message = "^refactor", group = "<!-- 2 -->๐Ÿšœ Refactor" },
+  { message = "^style", group = "<!-- 5 -->๐ŸŽจ Styling" },
+  { message = "^test", group = "<!-- 6 -->๐Ÿงช Testing" },
+  { body = ".*security", group = "<!-- 8 -->๐Ÿ›ก๏ธ Security" },
+  { message = "^revert", group = "<!-- 9 -->โ—€๏ธ Revert" },
+]
+# protect breaking changes from being skipped due to matching a skipping commit_parser
+protect_breaking_commits = false
+# filter out the commits that are not matched by commit parsers
+filter_commits = true
+# regex for matching git tags
+# tag_pattern = "v[0-9].*"
+# regex for skipping tags
+# skip_tags = ""
+# regex for ignoring tags
+# ignore_tags = ""
+# sort the tags topologically
+topo_order = false
+# sort the commits inside sections by oldest/newest order
+sort_commits = "oldest"
+# limit the number of commits included in the changelog.
+# limit_commits = 42
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..1587a669681c9a7e2aa097fbdc4b9882051fb58c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,3 @@
+{
+  "version": "1.0.0"
+}
diff --git a/version.txt b/version.txt
deleted file mode 100644
index 3eefcb9dd5b38e2c1dc061052455dd97bcd51e6c..0000000000000000000000000000000000000000
--- a/version.txt
+++ /dev/null
@@ -1 +0,0 @@
-1.0.0