aboutsummaryrefslogtreecommitdiff
path: root/src/infrastructure/scripts/cicd.sh
blob: 58f4fce36223ddc7bcc782a00ab017c40eccfcbc (plain) (blame)
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
167
168
#!/bin/sh
set -eu

usage() {
	cat <<-'EOF'
		Usage:
		  cicd [-n] NAME [SHA]
		  cicd -h
	EOF
}

help() {
	cat <<-'EOF'


		Options:
		  -n            build the system, but don't switch to it (dry-run)
		  -h, --help    show this message

		  NAME          the name of the project
		  SHA           the repository SHA to checkout (default: main)


		Do a CI/CD run of the project called NAME, located at
		/srv/git/$NAME.git.  If -n is given, only build, otherwise
		also do the deploy when the build is successfull.

		A "build" consists of:
		- doing a fresh clone of the project on a temporary directory;
		- checkout the project to version $SHA;
		- when a "manifest.scm" file exists in the root of the project,
		  use it to launch a containerized Guix shell; otherwise use a
		  fallback template for a containerized Guix shell;
		- build the "dev" target of the Makefile via "make dev".

		A "deploy" consists of:
		- copying the "description" metadata file to
		  /srv/git/$NAME.git/description, in order to update the project
		  repository's description;
		- upgrading the "pre-receive" Git hook, so that future runs are
		  affected by it;
		- copying the "public/" directory that the "dev" target built to
		  the /srv/www/s/$NAME/ directory, so that the projects "public/"
		  directory is accessible via the web address
		  "https://euandre.org/s/$NAME/".

		This command must be ran as root.


		Examples:

		  Build and deploy the "remembering" project on the default branch:

		    $ sudo cicd remembering


		  Build the "urubu" project on a specific commit, but don't deploy:

		    $ sudo cicd -n urubu 916dafc092f797349a54515756f2c8e477326511
	EOF
}


for flag in "$@"; do
	case "$flag" in
		--)
			break
			;;
		--help)
			usage
			help
			exit
			;;
		*)
			;;
	esac
done

DRY_RUN=false
while getopts 'nh' flag; do
	case "$flag" in
		n)
			DRY_RUN=true
			;;
		h)
			usage
			help
			exit
			;;
		*)
			usage >&2
			exit 2
			;;
	esac
done
shift $((OPTIND - 1))

NAME="${1:-}"
SHA="${2:-main}"
REPO="/srv/git/$NAME.git"

if [ -z "$NAME" ]; then
	printf 'Missing NAME.\n\n' >&2
	usage >&2
	exit 2
fi

if [ "$(id -un)" != 'root' ]; then
	printf 'This script must be run as root.\n\n' >&2
	usage >&2
	exit 2
fi


set +eu
# shellcheck source=/dev/null
. /etc/rc
set -eu


uuid() {
	od -xN20 /dev/urandom |
		head -n1 |
		awk '{OFS="-"; print $2$3,$4,$5,$6,$7$8$9}'
}

tmpname() {
	printf '%s/uuid-tmpname with spaces.%s' "${TMPDIR:-/tmp}" "$(uuid)"
}

mkdtemp() {
	name="$(tmpname)"
	mkdir -- "$name"
	printf '%s' "$name"
}


TMP="$(mkdtemp)"
trap 'rm -rf "$TMP"' EXIT


set -x
chown deployer:deployer "$TMP"
cd "$TMP"
sudo -u deployer git clone "$REPO" .
sudo -u deployer --preserve-env=GIT_CONFIG_GLOBAL git checkout "$SHA"
guix system describe

if [ -f manifest.scm ]; then
	guix shell -Cv3 -m manifest.scm -- make dev
else
	guix shell -Cv3                 -- make dev
fi

if [ "$DRY_RUN" = false ]; then
	# COMMENT: pre-receive is always running the previous version!
	#          The same is true for the reconfigure script itself.
	sudo cp description               "$REPO"/description
	sudo cp aux/ci/git-pre-receive.sh "$REPO"/hooks/pre-receive

	sudo -u deployer rsync \
		--delete                  \
		--chmod=D775,F664         \
		--chown=deployer:deployer \
		--exclude 'ci/*'          \
		-a                        \
		public/ /srv/www/s/"$NAME"/
fi