Skip to content

Commit

Permalink
ci: new automation script to update versions
Browse files Browse the repository at this point in the history
  • Loading branch information
sampaiodiego committed Nov 22, 2024
1 parent 3a4113f commit 64a2d00
Show file tree
Hide file tree
Showing 5 changed files with 358 additions and 0 deletions.
35 changes: 35 additions & 0 deletions .github/workflows/automatic-updates.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: Automatically update Docker image versions

on:
schedule:
- cron: "*/15 * * * *"

jobs:
build:
runs-on: ubuntu-latest
if: github.repository_owner == 'rocketchat'

steps:
- uses: actions/checkout@v4

- name: Run automation script
uses: actions/github-script@v7
id: updt
with:
result-encoding: string
script: |
const { default: script } = await import(`${process.env.GITHUB_WORKSPACE}/build-automation.mjs`);
return script(github);
- name: Create update PR
id: cpr
uses: peter-evans/create-pull-request@v6
with:
author: "rocketchat-github-ci <buildmaster@rocket.chat>"
branch: update-branch
base: master
commit-message: "feat: Rocket.Chat ${{ steps.updt.outputs.result }}"
title: "feat: Rocket.Chat ${{ steps.updt.outputs.result }}"
delete-branch: true
reviewers: |
sampaiodiego
84 changes: 84 additions & 0 deletions build-automation.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { promisify } from "util";

import child_process from "child_process";

const exec = promisify(child_process.exec);

const getCurrentVersions = async () => {
const versionsOutput = await getCurrentFolders();

const currentVersions = [];

await Promise.all(versionsOutput.trim().split(' ').map(async (folder) => {
const { stdout: fullVersionOutput } = await exec(`. ./functions.sh && get_full_version ${folder}`, { shell: "bash" });

currentVersions.push(fullVersionOutput.trim());
}));

return currentVersions;
};

const getSupportedVersions = async (github) => {
const { data: releases } = await github.request('https://releases.rocket.chat/v2/server/supportedVersions');

const stableReleases = releases.versions.filter(({ releaseType }) => releaseType === 'stable');

const groupedReleases = stableReleases.reduce((acc, { version }) => {
const minor = version.replace(/([0-9+])\.([0-9]+).*/, '$1.$2');
const patch = version.replace(/([0-9+])\.([0-9]+)\.([0-9]+).*/, '$3');

const latest = acc.get(minor) || 0;

acc.set(minor, latest > patch ? latest : patch);

return acc;
}, new Map());
return groupedReleases;
};

const removeCurrentVersions = async () => {
const versionsOutput = await getCurrentFolders();

await Promise.all(versionsOutput.trim().split(' ').map((folder) => exec(`rm -rf ./${folder}`, { shell: "bash" })));
}

const getCurrentFolders = async () => {
const { stdout } = await exec(". ./functions.sh && get_versions", { shell: "bash" });

return stdout;
};

export default async function(github) {
const supportedVersions = await getSupportedVersions(github);

const currentVersions = await getCurrentVersions();

const newVersions = Array
.from(supportedVersions)
.map(([minor, patch]) => `${minor}.${patch}`)
.filter((version) => !currentVersions.includes(version));

if (newVersions.length === 0) {
console.log('No new versions found. No update required.');
process.exit(0);
}

await removeCurrentVersions();

for await (const [minor, patch] of supportedVersions) {
const fullVersion = `${minor}.${patch}`;

const { data: info } = await github.request(`https://releases.rocket.chat/${fullVersion}/info`);

const { nodeVersion } = info;

const nodeMajor = nodeVersion.replace(/([0-9]+)\..*/, '$1');

await exec(`cp -r ./templates/node${nodeMajor} ${minor}`, { shell: "bash" });

await exec(`sed -ri 's/^(ENV RC_VERSION) .*/\\1 '"${fullVersion}"'/;' ${minor}/Dockerfile`, { shell: "bash" });
await exec(`sed -ri 's/^(ENV NODE_VERSION) .*/\\1 '"${nodeVersion}"'/;' ${minor}/Dockerfile`, { shell: "bash" });
}

return newVersions;
}
31 changes: 31 additions & 0 deletions functions.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
function get_versions() {
local versions=()
local dirs=()

if [ -n "$ZSH_VERSION" ]; then
IFS=' ' read -rA dirs <<< "$(echo "./"*/)"
else
IFS=' ' read -ra dirs <<< "$(echo "./"*/)"
fi

for dir in "${dirs[@]}"; do
if [ -f "${dir}/Dockerfile" ]; then
versions+=("${dir#./}")
fi
done

if [ ${#versions[@]} -gt 0 ]; then
echo "${versions[@]%/}"
fi
}

function get_full_version() {
local version
version=$1
shift

local default_dockerfile
default_dockerfile="${version}/Dockerfile"

grep -m1 'ENV RC_VERSION ' "${default_dockerfile}" | cut -d' ' -f3
}
96 changes: 96 additions & 0 deletions templates/node14/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
FROM debian:bullseye-slim

ENV RC_VERSION 6

## Installing Node.js
ENV NODE_ENV production
ENV NODE_VERSION 14

# Node installation based on https://github.com/nodejs/docker-node/blob/66b46292a6e5dd5856b1d5204dc51547c80eb17a/12/buster-slim/Dockerfile
RUN ARCH="x64" \
&& set -eux \
&& apt-get update && apt-get install -y --no-install-recommends ca-certificates curl wget gnupg dirmngr xz-utils \
&& rm -rf /var/lib/apt/lists/* \
&& for key in \
4ED778F539E3634C779C87C6D7062848A1AB005C \
94AE36675C464D64BAFA68DD7434390BDBE9B9C5 \
74F12602B6F1C4E913FAA37AD3A89613643B6201 \
71DCFD284A79C3B38668286BC97EC7A07EDE3FC1 \
8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \
C4F0DFFF4E8C1A8236409D08E73BC641CC11F4C8 \
C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C \
DD8F2338BAE7501E3DD5AC78C273792F7D83545D \
A48C2BEE680E841632CD4E44F07496B3EB3C1762 \
108F52B48DB57BB0CC439B2997B01419BD92F80A \
B9E2F5981AA6E0CD28160D9FF13993A75599653C \
; do \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \
done \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz" \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
&& rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& apt-mark auto '.*' > /dev/null \
&& find /usr/local -type f -executable -exec ldd '{}' ';' \
| awk '/=>/ { print $(NF-1) }' \
| sort -u \
| xargs -r dpkg-query --search \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual \
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false

RUN groupadd -r rocketchat \
&& useradd -r -g rocketchat rocketchat \
&& mkdir -p /app/uploads \
&& chown rocketchat:rocketchat /app/uploads

VOLUME /app/uploads

WORKDIR /app

RUN set -eux \
&& apt-get update \
&& apt-get install -y --no-install-recommends fontconfig \
&& aptMark="$(apt-mark showmanual)" \
&& apt-get install -y --no-install-recommends g++ make python3 ca-certificates curl gnupg \
&& rm -rf /var/lib/apt/lists/* \
# gpg: key 4FD08104: public key "Rocket.Chat Buildmaster <buildmaster@rocket.chat>" imported
&& gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 0E163286C20D07B9787EBE9FD7F9D0414FD08104 \
&& curl -fSL "https://releases.rocket.chat/${RC_VERSION}/download" -o rocket.chat.tgz \
&& curl -fSL "https://releases.rocket.chat/${RC_VERSION}/asc" -o rocket.chat.tgz.asc \
&& gpg --batch --verify rocket.chat.tgz.asc rocket.chat.tgz \
&& tar zxf rocket.chat.tgz \
&& rm rocket.chat.tgz rocket.chat.tgz.asc \
&& cd bundle/programs/server \
&& npm install --unsafe-perm=true \
&& apt-mark auto '.*' > /dev/null \
&& apt-mark manual $aptMark > /dev/null \
&& find /usr/local -type f -executable -exec ldd '{}' ';' \
| awk '/=>/ { print $(NF-1) }' \
| sort -u \
| xargs -r dpkg-query --search \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual \
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& npm cache clear --force \
&& chown -R rocketchat:rocketchat /app

USER rocketchat

WORKDIR /app/bundle

# needs a mongoinstance - defaults to container linking with alias 'db'
ENV DEPLOY_METHOD=docker-official \
MONGO_URL=mongodb://db:27017/meteor \
HOME=/tmp \
PORT=3000 \
ROOT_URL=http://localhost:3000

EXPOSE 3000

CMD ["node", "main.js"]
112 changes: 112 additions & 0 deletions templates/node20/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
FROM debian:bookworm-slim

ENV RC_VERSION 7

## Installing Node.js
ENV NODE_ENV production
ENV NODE_VERSION 20

# Node installation based on https://github.com/nodejs/docker-node/blob/8483b3edd9cc2a38360d88d360e3093d657ac3fe/20/bookworm/Dockerfile
RUN ARCH= && dpkgArch="$(dpkg --print-architecture)" \
&& case "${dpkgArch##*-}" in \
amd64) ARCH='x64';; \
ppc64el) ARCH='ppc64le';; \
s390x) ARCH='s390x';; \
arm64) ARCH='arm64';; \
armhf) ARCH='armv7l';; \
i386) ARCH='x86';; \
*) echo "unsupported architecture"; exit 1 ;; \
esac \
# use pre-existing gpg directory, see https://github.com/nodejs/docker-node/pull/1895#issuecomment-1550389150
&& export GNUPGHOME="$(mktemp -d)" \
&& set -ex \
&& apt-get update && apt-get install -y --no-install-recommends ca-certificates curl wget gnupg dirmngr xz-utils && rm -rf /var/lib/apt/lists/* \
# gpg keys listed at https://github.com/nodejs/node#release-keys
&& for key in \
4ED778F539E3634C779C87C6D7062848A1AB005C \
141F07595B7B3FFE74309A937405533BE57C7D57 \
74F12602B6F1C4E913FAA37AD3A89613643B6201 \
DD792F5973C6DE52C432CBDAC77ABFA00DDBF2B7 \
CC68F5A3106FF448322E48ED27F5E38D5B0A215F \
8FCCA13FEF1D0C2E91008E09770F7A9A5AE15600 \
890C08DB8579162FEE0DF9DB8BEAB4DFCF555EF4 \
C82FA3AE1CBEDC6BE46B9360C43CEC45C17AB93C \
108F52B48DB57BB0CC439B2997B01419BD92F80A \
A363A499291CBBC940DD62E41F10027AF002F8B0 \
; do \
gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys "$key" || \
gpg --batch --keyserver keyserver.ubuntu.com --recv-keys "$key" ; \
done \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-$ARCH.tar.xz" \
&& curl -fsSLO --compressed "https://nodejs.org/dist/v$NODE_VERSION/SHASUMS256.txt.asc" \
&& gpg --batch --decrypt --output SHASUMS256.txt SHASUMS256.txt.asc \
&& gpgconf --kill all \
&& rm -rf "$GNUPGHOME" \
&& grep " node-v$NODE_VERSION-linux-$ARCH.tar.xz\$" SHASUMS256.txt | sha256sum -c - \
&& tar -xJf "node-v$NODE_VERSION-linux-$ARCH.tar.xz" -C /usr/local --strip-components=1 --no-same-owner \
&& rm "node-v$NODE_VERSION-linux-$ARCH.tar.xz" SHASUMS256.txt.asc SHASUMS256.txt \
&& ln -s /usr/local/bin/node /usr/local/bin/nodejs \
# smoke tests
&& node --version \
&& npm --version \
&& apt-mark auto '.*' > /dev/null \
&& find /usr/local -type f -executable -exec ldd '{}' ';' \
| awk '/=>/ { print $(NF-1) }' \
| sort -u \
| xargs -r dpkg-query --search \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual \
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false

RUN groupadd -r rocketchat \
&& useradd -r -g rocketchat rocketchat \
&& mkdir -p /app/uploads \
&& chown rocketchat:rocketchat /app/uploads

VOLUME /app/uploads

WORKDIR /app

RUN set -eux \
&& apt-get update \
&& apt-get install -y --no-install-recommends fontconfig \
&& aptMark="$(apt-mark showmanual)" \
&& apt-get install -y --no-install-recommends g++ make python3 ca-certificates curl gnupg \
&& rm -rf /var/lib/apt/lists/* \
# gpg: key 4FD08104: public key "Rocket.Chat Buildmaster <buildmaster@rocket.chat>" imported
&& gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 0E163286C20D07B9787EBE9FD7F9D0414FD08104 \
&& curl -fSL "https://releases.rocket.chat/${RC_VERSION}/download" -o rocket.chat.tgz \
&& curl -fSL "https://releases.rocket.chat/${RC_VERSION}/asc" -o rocket.chat.tgz.asc \
&& gpg --batch --verify rocket.chat.tgz.asc rocket.chat.tgz \
&& tar zxf rocket.chat.tgz \
&& rm rocket.chat.tgz rocket.chat.tgz.asc \
&& cd bundle/programs/server \
&& npm install --unsafe-perm=true \
&& apt-mark auto '.*' > /dev/null \
&& apt-mark manual $aptMark > /dev/null \
&& find /usr/local -type f -executable -exec ldd '{}' ';' \
| awk '/=>/ { print $(NF-1) }' \
| sort -u \
| xargs -r dpkg-query --search \
| cut -d: -f1 \
| sort -u \
| xargs -r apt-mark manual \
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
&& npm cache clear --force \
&& chown -R rocketchat:rocketchat /app

USER rocketchat

WORKDIR /app/bundle

# needs a mongoinstance - defaults to container linking with alias 'db'
ENV DEPLOY_METHOD=docker-official \
MONGO_URL=mongodb://db:27017/meteor \
HOME=/tmp \
PORT=3000 \
ROOT_URL=http://localhost:3000

EXPOSE 3000

CMD ["node", "main.js"]

0 comments on commit 64a2d00

Please sign in to comment.