Skip to content

Place FOSS license stub on bundle path #10

Place FOSS license stub on bundle path

Place FOSS license stub on bundle path #10

name: DevSH FOSS image
on:
push:
branches:
- devsh
paths-ignore:
- "**.md"
workflow_dispatch:
inputs:
image_tag:
description: Image tag to publish
required: false
default: ""
permissions:
contents: read
packages: write
concurrency:
group: devsh-foss-image-${{ github.ref }}
cancel-in-progress: true
env:
IMAGE: ghcr.io/devsh-graphics-programming/rocketchat-foss
METEOR_PROFILE: "1000"
BABEL_ENV: production
jobs:
build:
name: Build and publish FOSS image
runs-on: ubuntu-24.04
timeout-minutes: 180
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- name: Resolve image tag
id: image
run: |
set -euo pipefail
requested='${{ inputs.image_tag }}'
if [ -n "${requested}" ]; then
image_tag="${requested}"
else
base_version=$(python3 -c 'import json; print(json.load(open("package.json"))["version"])')
image_tag="${base_version}-devsh.$(date -u +%Y%m%d%H%M%S).${GITHUB_SHA:0:12}"
fi
echo "image-tag=${image_tag}" >> "${GITHUB_OUTPUT}"
echo "IMAGE_TAG=${image_tag}" >> "${GITHUB_ENV}"
echo "Publishing ${IMAGE}:${image_tag}"
- name: Validate image tag
run: |
set -euo pipefail
case "${IMAGE_TAG}" in
[0-9]*.[0-9]*.[0-9]*-devsh.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9].*) ;;
*) echo "Invalid image tag for DevSH rollout: ${IMAGE_TAG}" >&2; exit 1 ;;
esac
- name: Resolve source cache key
id: source
run: |
set -euo pipefail
SOURCE_HASH=$(
git ls-files \
':(exclude).github/**' \
':(exclude)**/*.md' \
| sort \
| while IFS= read -r file; do sha256sum "$file"; done \
| sha256sum \
| awk '{ print $1 }'
)
echo "hash=${SOURCE_HASH}" >> "${GITHUB_OUTPUT}"
echo "Source cache key: ${SOURCE_HASH}"
- name: Free runner disk space
run: |
set -euo pipefail
df -h
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android /opt/hostedtoolcache/CodeQL || true
docker system prune -af || true
df -h
- name: Cache Meteor bundle
id: cache-bundle
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: /tmp/Rocket.Chat.tar.gz
key: ${{ runner.os }}-${{ runner.arch }}-devsh-foss-bundle-${{ steps.source.outputs.hash }}
- name: Restore cached Meteor bundle
if: steps.cache-bundle.outputs.cache-hit == 'true'
run: |
set -euo pipefail
mkdir -p /tmp/dist
tar -xzf /tmp/Rocket.Chat.tar.gz -C /tmp/dist
du -sh /tmp/dist/bundle
- name: Add swap
if: steps.cache-bundle.outputs.cache-hit != 'true'
run: |
set -euo pipefail
if swapon --show=NAME --noheadings | grep -qx '/swapfile'; then
echo "/swapfile is already active"
free -h
exit 0
fi
swapfile="/mnt/devsh-swapfile-${GITHUB_RUN_ID}"
sudo fallocate -l 8G "${swapfile}" || sudo dd if=/dev/zero of="${swapfile}" bs=1M count=8192
sudo chmod 600 "${swapfile}"
sudo mkswap "${swapfile}"
sudo swapon "${swapfile}"
free -h
- name: Cache Vite
if: steps.cache-bundle.outputs.cache-hit != 'true'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ./node_modules/.vite
key: vite-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('package.json') }}
restore-keys: |
vite-${{ runner.os }}-${{ runner.arch }}-
- name: Cache Meteor
if: steps.cache-bundle.outputs.cache-hit != 'true'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ~/.meteor
key: meteor-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('apps/meteor/.meteor/release') }}
restore-keys: |
meteor-${{ runner.os }}-${{ runner.arch }}-
- name: Cache Meteor local
if: steps.cache-bundle.outputs.cache-hit != 'true'
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ./apps/meteor/.meteor/local
key: meteor-local-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('apps/meteor/.meteor/versions') }}
restore-keys: |
meteor-local-${{ runner.os }}-${{ runner.arch }}-
- name: Setup NodeJS
if: steps.cache-bundle.outputs.cache-hit != 'true'
uses: ./.github/actions/setup-node
with:
cache-modules: true
install: true
type: development
HARDENED_MODE: "0"
- name: Install Meteor
if: steps.cache-bundle.outputs.cache-hit != 'true'
run: |
set -euo pipefail
set +e
METEOR_SYMLINK_TARGET=$(readlink ~/.meteor/meteor)
METEOR_TOOL_DIRECTORY=$(dirname "$METEOR_SYMLINK_TARGET")
set -e
LAUNCHER=$HOME/.meteor/$METEOR_TOOL_DIRECTORY/scripts/admin/launch-meteor
if [ -e "$LAUNCHER" ]; then
sudo cp "$LAUNCHER" /usr/local/bin/meteor
fi
command -v meteor >/dev/null 2>&1 || curl https://install.meteor.com | sed s/--progress-bar/-sL/g | /bin/sh
meteor --version
- name: Cache Turbo
if: steps.cache-bundle.outputs.cache-hit != 'true'
uses: rharkor/caching-for-turbo@00a0515f175df9fd2e15c4560144ad5fdbebb0c7 # v2.3.13
- name: Build shared packages
if: steps.cache-bundle.outputs.cache-hit != 'true'
run: yarn build
- name: Convert checkout to FOSS build
if: steps.cache-bundle.outputs.cache-hit != 'true'
run: printf 'y\n' | yarn fossify
- name: Build Meteor bundle
if: steps.cache-bundle.outputs.cache-hit != 'true'
run: yarn workspace @rocket.chat/meteor build:ci
- name: Reduce bundle size
if: steps.cache-bundle.outputs.cache-hit != 'true'
run: |
set -euo pipefail
find /tmp/dist/bundle -type f -name "*.d.ts" -delete
find /tmp/dist/bundle/programs/server/npm/node_modules -type f -name "*.map" -delete || true
find /tmp/dist/bundle/programs/web.browser -type f -name "*.map" -delete || true
du -sh /tmp/dist/bundle
- name: Install FOSS runtime license stub
run: |
set -euo pipefail
for license_dir in \
/tmp/dist/bundle/node_modules/@rocket.chat/license \
/tmp/dist/bundle/programs/server/npm/node_modules/@rocket.chat/license
do
mkdir -p "${license_dir}/src/validation"
cat > "${license_dir}/package.json" <<'JSON'
{
"name": "@rocket.chat/license",
"version": "0.0.0-devsh-foss",
"main": "index.js"
}
JSON
cat > "${license_dir}/index.js" <<'JS'
class DuplicatedLicenseError extends Error {
constructor(message = 'Duplicated license') {
super(message);
this.name = 'DuplicatedLicense';
}
}
const noop = () => undefined;
const noopAsync = async () => undefined;
const falseSync = () => false;
const falseAsync = async () => false;
const emptyArray = () => [];
const License = {
encryptedLicense: undefined,
validateFormat: () => true,
hasModule: falseSync,
getModules: emptyArray,
getExternalModules: emptyArray,
getModuleDefinition: () => undefined,
getTags: emptyArray,
hasValidLicense: falseSync,
getLicense: noopAsync,
getGuestPermissions: noopAsync,
shouldPreventAction: falseAsync,
isLimitReached: falseAsync,
onValidFeature: noop,
onInvalidFeature: noop,
onToggledFeature: noop,
onModule: noop,
onValidateLicense: noop,
onInvalidateLicense: noop,
onLimitReached: noop,
onBehaviorTriggered: noop,
onLicense: noop,
getMaxActiveUsers: () => 0,
getAppsConfig: () => ({}),
getUnmodifiedLicenseAndModules: noop,
overwriteClassOnLicense: (value) => value,
setLicenseLimitCounter: noop,
getCurrentValueForLicenseLimit: async () => 0,
remove: noop,
setLicense: falseAsync,
revalidateLicense: noopAsync,
sync: noopAsync
};
const AirGappedRestriction = {
restricted: false,
computeRestriction: noopAsync,
isWarningPeriod: falseSync,
on: noop,
off: noop
};
const applyLicense = falseAsync;
const applyLicenseOrRemove = falseAsync;
class LicenseManager {}
class MockedLicenseBuilder {}
module.exports = {
AirGappedRestriction,
DuplicatedLicenseError,
License,
LicenseManager,
MockedLicenseBuilder,
applyLicense,
applyLicenseOrRemove
};
JS
cat > "${license_dir}/src/validation/validateLimit.js" <<'JS'
module.exports = {
validateLimit: () => false,
validateWarnLimit: () => false
};
JS
done
- name: Store Meteor bundle cache artifact
if: steps.cache-bundle.outputs.cache-hit != 'true'
run: |
set -euo pipefail
cd /tmp/dist
tar -czf /tmp/Rocket.Chat.tar.gz bundle
ls -lh /tmp/Rocket.Chat.tar.gz
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
- name: Build Docker image
run: |
set -euo pipefail
docker buildx build \
--load \
--cache-from type=gha \
--cache-to type=gha,mode=max \
--label "org.opencontainers.image.source=https://github.com/Devsh-Graphics-Programming/Rocket.Chat" \
--label "org.opencontainers.image.revision=${GITHUB_SHA}" \
--tag "${IMAGE}:${IMAGE_TAG}" \
--tag "${IMAGE}:devsh" \
--file "${GITHUB_WORKSPACE}/apps/meteor/.docker/Dockerfile.alpine" \
/tmp/dist
- name: Login to GHCR
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push Docker image
run: |
set -euo pipefail
docker push "${IMAGE}:${IMAGE_TAG}"
docker push "${IMAGE}:devsh"