Place FOSS license stub on bundle path #10
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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" |