Skip to content

Commit afdbcba

Browse files
authored
Merge pull request #1048 from nextcloud/backport/upgrade-testings
Backport: upgrade testings
2 parents 26fddc2 + 3d364a9 commit afdbcba

8 files changed

Lines changed: 515 additions & 50 deletions

File tree

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#!/bin/bash
2+
# SPDX-FileCopyrightText: 2026 Jankari Tech Pvt. Ltd.
3+
# SPDX-License-Identifier: AGPL-3.0-or-later
4+
5+
# This script is used to build the upgradable integration_openproject app. It performs the following steps:
6+
# 1. Copy the build files to a separate folder named publish, excluding unnecessary files and directories.
7+
# 2. Get the current version of the app and update it to a new version by incrementing the major version number.
8+
# 3. Sign the app files using self-signed certificate.
9+
# 4. Archive the app into a .tar.gz file.
10+
# 5. Sign the archive.
11+
# NOTE: Before running this script, ensure that the Nextcloud instance is running and integration_openproject app is built.
12+
13+
# Required environment variables:
14+
# 1. NEXTCLOUD_PATH (Absolute path to Nextcloud where occ command is available, e.g. /var/www/html)
15+
# 2. INTEGRATION_OPENPROJECT_DIR (Absolute path to the directory containing the integration_openproject repository, e.g. /var/www/html/build-app-shared)
16+
17+
set -e -o pipefail
18+
19+
# helper functions
20+
log_error() {
21+
echo -e "\e[31m$1\e[0m"
22+
}
23+
24+
log_info() {
25+
echo -e "\e[37m$1\e[0m"
26+
}
27+
28+
log_success() {
29+
echo -e "\e[32m$1\e[0m"
30+
}
31+
32+
if [[ -z "$NEXTCLOUD_PATH" ]] || [[ -z "$INTEGRATION_OPENPROJECT_DIR" ]]; then
33+
log_error "Missing required environment variables: NEXTCLOUD_PATH, INTEGRATION_OPENPROJECT_DIR"
34+
exit 1
35+
fi
36+
37+
APP_ID=integration_openproject
38+
cd "$INTEGRATION_OPENPROJECT_DIR"
39+
40+
if [[ ! -d "$INTEGRATION_OPENPROJECT_DIR/$APP_ID" ]]; then
41+
log_error "Folder does not exist: $INTEGRATION_OPENPROJECT_DIR/$APP_ID"
42+
exit 1
43+
fi
44+
45+
mkdir -p publish
46+
47+
# copy app files to a separate folder
48+
log_info "Copying necessary app files to publish directory..."
49+
rsync -a \
50+
--exclude=server \
51+
--exclude=dev \
52+
--exclude=.git \
53+
--exclude=appinfo/signature.json \
54+
--exclude='*.swp' \
55+
--exclude=build \
56+
--exclude=.gitignore \
57+
--exclude=.travis.yml \
58+
--exclude=.scrutinizer.yml \
59+
--exclude=CONTRIBUTING.md \
60+
--exclude=composer.phar \
61+
--exclude=js/node_modules \
62+
--exclude=node_modules \
63+
--exclude=src \
64+
--exclude=translationfiles \
65+
--exclude='webpack.*' \
66+
--exclude=stylelint.config.js \
67+
--exclude=.eslintrc.js \
68+
--exclude=.github \
69+
--exclude=.gitlab-ci.yml \
70+
--exclude=crowdin.yml \
71+
--exclude=tools \
72+
--exclude=.tx \
73+
--exclude=.l10nignore \
74+
--exclude=l10n/.tx \
75+
--exclude=l10n/l10n.pl \
76+
--exclude=l10n/templates \
77+
--exclude='l10n/*.sh' \
78+
--exclude='l10n/[a-z][a-z]' \
79+
--exclude='l10n/[a-z][a-z]_[A-Z][A-Z]' \
80+
--exclude=l10n/no-php \
81+
--exclude=makefile \
82+
--exclude=screenshots \
83+
--exclude='phpunit*xml' \
84+
--exclude=tests \
85+
--exclude=ci \
86+
--exclude=vendor/bin \
87+
$APP_ID publish/
88+
89+
cd publish
90+
91+
# get current version of integration_openproject and update to new version
92+
current_version=$(php ${NEXTCLOUD_PATH}/occ app:list --output=json | jq -r ".enabled.$APP_ID") || { log_error "Failed to get current version of $APP_ID app."; exit 1; }
93+
IFS=. read -r a b c <<< "$current_version"
94+
NEXT_APP_VERSION="$((a+1)).$b.$c"
95+
96+
# Save the new tag to a file for later use in the workflow
97+
echo "$NEXT_APP_VERSION" > "${APP_ID}_new_version.txt"
98+
99+
# update version in info.xml
100+
sed -i "s|<version>.*</version>|<version>$NEXT_APP_VERSION</version>|" "integration_openproject/appinfo/info.xml"
101+
102+
#####################
103+
# Signing the app #
104+
#####################
105+
# https://nextcloudappstore.readthedocs.io/en/latest/developer.html#obtaining-a-certificate
106+
# Check if openssl exists, otherwise install it
107+
if ! command -v openssl >/dev/null 2>&1; then
108+
echo "OpenSSL not found. Installing..."
109+
apt update && apt install -y openssl || {
110+
echo "Failed to install OpenSSL."
111+
exit 1
112+
}
113+
fi
114+
log_info "Generating app.key and app.crt..."
115+
openssl req -x509 -newkey rsa:4096 -sha256 -nodes \
116+
-keyout app.key \
117+
-out app.crt \
118+
-days 3650 \
119+
-subj "/CN=$APP_ID" \
120+
-addext "basicConstraints=CA:FALSE" \
121+
-addext "keyUsage=digitalSignature" \
122+
-addext "extendedKeyUsage=codeSigning"
123+
124+
if [[ ! -s app.key || ! -s app.crt ]]; then
125+
log_error "Failed to generate app signing certificate and key: app.key or app.crt not found."
126+
exit 1
127+
fi
128+
129+
log_info "Adding the generated certificate to Nextcloud's root.crt..."
130+
nextcloud_root_crt="${NEXTCLOUD_PATH}/resources/codesigning/root.crt"
131+
if [[ -f ${nextcloud_root_crt} ]]; then
132+
echo "" >> ${nextcloud_root_crt}
133+
cat app.crt >> ${nextcloud_root_crt}
134+
else
135+
log_error "Nextcloud's root.crt not found at ${nextcloud_root_crt}."
136+
exit 1
137+
fi
138+
139+
# fix permissions for signing
140+
chown www-data app.key
141+
chown www-data app.crt
142+
chown -R www-data $APP_ID
143+
144+
# Sign the app
145+
# need full path for signing
146+
log_info "Signing the app files..."
147+
php ${NEXTCLOUD_PATH}/occ integrity:sign-app \
148+
--privateKey=${INTEGRATION_OPENPROJECT_DIR}/publish/app.key \
149+
--certificate=${INTEGRATION_OPENPROJECT_DIR}/publish/app.crt \
150+
--path=${INTEGRATION_OPENPROJECT_DIR}/publish/$APP_ID || { log_error "Failed to sign app."; exit 1; }
151+
152+
# Archive the app
153+
tar -czf $APP_ID-$NEXT_APP_VERSION.tar.gz $APP_ID
154+
if [[ ! -f $APP_ID-$NEXT_APP_VERSION.tar.gz ]]; then
155+
log_error "Failed to archive the app. Archive file $APP_ID-$NEXT_APP_VERSION.tar.gz not found."
156+
exit 1
157+
fi
158+
log_success "App archived into $APP_ID-$NEXT_APP_VERSION.tar.gz."
159+
160+
#####################
161+
# Sign the archive #
162+
#####################
163+
log_info "Signing the app archive..."
164+
openssl dgst -sha512 -sign app.key $APP_ID-$NEXT_APP_VERSION.tar.gz \
165+
| openssl base64 \
166+
| tee ${INTEGRATION_OPENPROJECT_DIR}/publish/sign.txt
167+
168+
if [[ ! -s ${INTEGRATION_OPENPROJECT_DIR}/publish/sign.txt ]]; then
169+
log_error "Failed to sign the app archive. Signature file sign.txt is empty or not found."
170+
exit 1
171+
else
172+
log_success "App archive signed successfully."
173+
fi
174+
175+
log_success "Upgradable app built successfully."
176+
177+
# prepare apps.json file
178+
if [[ ! -f ${INTEGRATION_OPENPROJECT_DIR}/publish/${APP_ID}/appinfo/signature.json ]]; then
179+
echo "Signature file not found at ${INTEGRATION_OPENPROJECT_DIR}/publish/${APP_ID}/appinfo/signature.json."
180+
exit 1
181+
fi
182+
certificate=$(jq '.certificate' "${INTEGRATION_OPENPROJECT_DIR}/publish/${APP_ID}/appinfo/signature.json")
183+
signature=$(tr -d '\n' < "${INTEGRATION_OPENPROJECT_DIR}/publish/sign.txt")
184+
185+
# Create apps.json with the required structure
186+
cat > apps.json <<EOF
187+
[
188+
{
189+
"id": "$APP_ID",
190+
"releases": [
191+
{
192+
"version": "$NEXT_APP_VERSION",
193+
"minIntSize": 32,
194+
"download": "http://localhost:8080/${APP_ID}-${NEXT_APP_VERSION}.tar.gz",
195+
"licenses": [
196+
"agpl"
197+
],
198+
"isNightly": false,
199+
"rawPlatformVersionSpec": "\u003E=28",
200+
"signature": "$signature",
201+
"signatureDigest": "sha512"
202+
}
203+
],
204+
"certificate": $certificate
205+
}
206+
]
207+
EOF

.github/scripts/notify-to-element.sh

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# SPDX-FileCopyrightText: 2023-2024 Jankari Tech Pvt. Ltd.
33
# SPDX-License-Identifier: AGPL-3.0-or-later
44

5+
set -e
6+
57
# helper functions
68
log_error() {
79
echo -e "\e[31m$1\e[0m"
@@ -15,42 +17,65 @@ log_success() {
1517
echo -e "\e[32m$1\e[0m"
1618
}
1719

18-
log_info "Fetching all workflow jobs....."
20+
required_vars=(
21+
ELEMENT_CHAT_URL
22+
ELEMENT_ROOM_ID
23+
NIGHTLY_CI_USER_TOKEN
24+
GITHUB_REPOSITORY
25+
GITHUB_RUN_ID
26+
BRANCH_NAME
27+
NEEDS_JSON
28+
)
1929

20-
response=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \
21-
"https://api.github.com/repos/$REPO_OWNER/$REPO_NAME/actions/runs/$RUN_ID/jobs?per_page=50")
30+
for var in "${required_vars[@]}"; do
31+
if [[ -z "${!var}" ]]; then
32+
log_error "❌ Missing required environment variable: $var"
33+
log_info ""
34+
log_info "Required environment variables:"
35+
log_info "- ELEMENT_CHAT_URL : URL of the Element chat (e.g. https://matrix.element.io)"
36+
log_info "- ELEMENT_ROOM_ID : Matrix room ID (e.g. abcdefg:matrix.element.io)"
37+
log_info "- NIGHTLY_CI_USER_TOKEN : Access token for sending messages (e.g. "sometoken")"
38+
log_info "- GITHUB_REPOSITORY : GitHub repository (e.g. user/repo) set by GitHub Actions environment variable"
39+
log_info "- GITHUB_RUN_ID : GitHub run ID (e.g. 123456789) set by GitHub Actions environment variable"
40+
log_info "- BRANCH_NAME : Branch name (e.g. master)"
41+
log_info "- NEEDS_JSON : JSON string containing job results"
42+
log_info ""
43+
exit 1
44+
fi
45+
done
2246

23-
log_info "Fetching jobs informations succeeded!
24-
"
25-
if [[ "$response" != *"jobs"* ]]; then
26-
log_error "No jobs found in the below response!"
27-
log_info "$response"
47+
jobs=$(echo "$NEEDS_JSON" | jq -r 'keys[]' 2>/dev/null)
48+
if [[ -z "$jobs" ]]; then
49+
log_error "❌ No jobs found in below JSON:"
50+
log_info "$NEEDS_JSON"
2851
exit 1
2952
fi
3053

31-
jobs_informations=$(echo "$response" | jq '.jobs[:-1]')
32-
jobs_conclusions=$(echo "$jobs_informations" | jq -r '.[].conclusion')
54+
results=$(echo "$NEEDS_JSON" | jq -r '.[].result' 2>/dev/null)
3355

34-
workflow_status="Success"
35-
if [[ " ${jobs_conclusions[*]} " == *"failure"* ]]; then
36-
workflow_status="Failure"
37-
elif [[ " ${jobs_conclusions[*]} " == *"cancelled"* ]]; then
38-
workflow_status="Cancelled"
39-
elif [[ " ${jobs_conclusions[*]} " == *"skipped"* ]]; then
40-
workflow_status="Skipped"
56+
workflow_status="Success"
57+
if [[ "${results[*]}" == *"failure"* ]]; then
58+
workflow_status="Failure"
59+
elif [[ "${results[*]}" == *"cancelled"* ]]; then
60+
workflow_status="⚠️ Cancelled"
61+
elif [[ "${results[*]}" == *"skipped"* ]]; then
62+
workflow_status="⚠️ Skipped"
4163
fi
4264

4365
log_info "Sending report to the element chat...."
4466

67+
payload=$(cat <<EOF
68+
{
69+
"msgtype": "m.text",
70+
"body": "",
71+
"format": "org.matrix.custom.html",
72+
"formatted_body": "<a href=\"https://github.com/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}\">NC-Nightly-${BRANCH_NAME}</a><br></br><b>Status: ${workflow_status}</b>"
73+
}
74+
EOF
75+
)
76+
4577
send_message_to_room_response=$(curl -s -XPOST "$ELEMENT_CHAT_URL/_matrix/client/r0/rooms/%21$ELEMENT_ROOM_ID/send/m.room.message?access_token=$NIGHTLY_CI_USER_TOKEN" \
46-
-d '
47-
{
48-
"msgtype": "m.text",
49-
"body": "",
50-
"format": "org.matrix.custom.html",
51-
"formatted_body": "<a href=\"https://github.com/'$REPO_OWNER'/'$REPO_NAME'/actions/runs/'$RUN_ID'\">NC-Nightly-'$BRANCH_NAME'</a><br></br><b><i>Status: '$workflow_status'</i></b>"
52-
}
53-
'
78+
-d "$payload"
5479
)
5580

5681
if [[ "$send_message_to_room_response" != *"event_id"* ]]; then
@@ -59,4 +84,4 @@ if [[ "$send_message_to_room_response" != *"event_id"* ]]; then
5984
exit 1
6085
fi
6186

62-
log_success "Notification of the nightly build has been sent to Element chat (OpenProject + Nextcloud)"
87+
log_success "Notification of the nightly build has been sent to Element chat (OpenProject + Nextcloud)"

0 commit comments

Comments
 (0)