From bf753c1a8f0637523cd101ff0c688664544582bb Mon Sep 17 00:00:00 2001 From: QIQGOOD Date: Sun, 27 Jul 2025 08:27:36 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=BA=86=E4=B8=80=E4=BA=9B?= =?UTF-8?q?=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CODE_OF_CONDUCT.md | 128 ---- CONTRIBUTING.md | 245 ------- SECURITY.md | 39 - deployments/aio/community/Dockerfile | 68 -- deployments/aio/community/README.md | 174 ----- deployments/aio/community/build.sh | 155 ---- deployments/aio/community/start.sh | 169 ----- deployments/aio/community/supervisor.conf | 124 ---- deployments/aio/community/variables.env | 53 -- deployments/cli/community/README.md | 630 ---------------- deployments/cli/community/build.yml | 36 - deployments/cli/community/docker-compose.yml | 254 ------- deployments/cli/community/images/download.png | Bin 22985 -> 0 bytes .../cli/community/images/migrate-error.png | Bin 14139 -> 0 bytes deployments/cli/community/images/restart.png | Bin 15664 -> 0 bytes deployments/cli/community/images/started.png | Bin 21489 -> 0 bytes deployments/cli/community/images/stopped.png | Bin 16638 -> 0 bytes deployments/cli/community/images/upgrade.png | Bin 55240 -> 0 bytes deployments/cli/community/install.sh | 681 ------------------ .../cli/community/migration-0.13-0.14.sh | 118 --- .../cli/community/restore-airgapped.sh | 144 ---- deployments/cli/community/restore.sh | 123 ---- deployments/cli/community/variables.env | 79 -- deployments/kubernetes/community/README.md | 5 - deployments/swarm/community/swarm.sh | 611 ---------------- 25 files changed, 3836 deletions(-) delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md delete mode 100644 SECURITY.md delete mode 100644 deployments/aio/community/Dockerfile delete mode 100644 deployments/aio/community/README.md delete mode 100755 deployments/aio/community/build.sh delete mode 100644 deployments/aio/community/start.sh delete mode 100644 deployments/aio/community/supervisor.conf delete mode 100644 deployments/aio/community/variables.env delete mode 100644 deployments/cli/community/README.md delete mode 100644 deployments/cli/community/build.yml delete mode 100644 deployments/cli/community/docker-compose.yml delete mode 100644 deployments/cli/community/images/download.png delete mode 100644 deployments/cli/community/images/migrate-error.png delete mode 100644 deployments/cli/community/images/restart.png delete mode 100644 deployments/cli/community/images/started.png delete mode 100644 deployments/cli/community/images/stopped.png delete mode 100644 deployments/cli/community/images/upgrade.png delete mode 100755 deployments/cli/community/install.sh delete mode 100755 deployments/cli/community/migration-0.13-0.14.sh delete mode 100755 deployments/cli/community/restore-airgapped.sh delete mode 100755 deployments/cli/community/restore.sh delete mode 100644 deployments/cli/community/variables.env delete mode 100644 deployments/kubernetes/community/README.md delete mode 100755 deployments/swarm/community/swarm.sh diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 9fa847b6e76..00000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,128 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, religion, or sexual identity -and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -- Focusing on what is best not just for us as individuals, but for the - overall community - -Examples of unacceptable behavior include: - -- The use of sexualized language or imagery, and sexual attention or - advances of any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email - address, without their explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official e-mail address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -squawk@plane.so. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series -of actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or -permanent ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within -the community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.0, available at -https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. - -Community Impact Guidelines were inspired by [Mozilla's code of conduct -enforcement ladder](https://github.com/mozilla/diversity). - -[homepage]: https://www.contributor-covenant.org - -For answers to common questions about this code of conduct, see the FAQ at -https://www.contributor-covenant.org/faq. Translations are available at -https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index c401c3c2cf5..00000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,245 +0,0 @@ -# Contributing to Plane - -Thank you for showing an interest in contributing to Plane! All kinds of contributions are valuable to us. In this guide, we will cover how you can quickly onboard and make your first contribution. - -## Submitting an issue - -Before submitting a new issue, please search the [issues](https://github.com/makeplane/plane/issues) tab. Maybe an issue or discussion already exists and might inform you of workarounds. Otherwise, you can give new information. - -While we want to fix all the [issues](https://github.com/makeplane/plane/issues), before fixing a bug we need to be able to reproduce and confirm it. Please provide us with a minimal reproduction scenario using a repository or [Gist](https://gist.github.com/). Having a live, reproducible scenario gives us the information without asking questions back & forth with additional questions like: - -- 3rd-party libraries being used and their versions -- a use-case that fails - -Without said minimal reproduction, we won't be able to investigate all [issues](https://github.com/makeplane/plane/issues), and the issue might not be resolved. - -You can open a new issue with this [issue form](https://github.com/makeplane/plane/issues/new). - -### Naming conventions for issues - -When opening a new issue, please use a clear and concise title that follows this format: - -- For bugs: `πŸ› Bug: [short description]` -- For features: `πŸš€ Feature: [short description]` -- For improvements: `πŸ› οΈ Improvement: [short description]` -- For documentation: `πŸ“˜ Docs: [short description]` - -**Examples:** - -- `πŸ› Bug: API token expiry time not saving correctly` -- `πŸ“˜ Docs: Clarify RAM requirement for local setup` -- `πŸš€ Feature: Allow custom time selection for token expiration` - -This helps us triage and manage issues more efficiently. - -## Projects setup and Architecture - -### Requirements - -- Docker Engine installed and running -- Node.js version 20+ [LTS version](https://nodejs.org/en/about/previous-releases) -- Python version 3.8+ -- Postgres version v14 -- Redis version v6.2.7 -- **Memory**: Minimum **12 GB RAM** recommended - > ⚠️ Running the project on a system with only 8 GB RAM may lead to setup failures or memory crashes (especially during Docker container build/start or dependency install). Use cloud environments like GitHub Codespaces or upgrade local RAM if possible. - -### Setup the project - -The project is a monorepo, with backend api and frontend in a single repo. - -The backend is a django project which is kept inside apps/api - -1. Clone the repo - -```bash -git clone https://github.com/makeplane/plane.git [folder-name] -cd [folder-name] -chmod +x setup.sh -``` - -2. Run setup.sh - -```bash -./setup.sh -``` - -3. Start the containers - -```bash -docker compose -f docker-compose-local.yml up -``` - -4. Start web apps: - -```bash -yarn dev -``` - -5. Open your browser to http://localhost:3001/god-mode/ and register yourself as instance admin -6. Open up your browser to http://localhost:3000 then log in using the same credentials from the previous step - -That’s it! You’re all set to begin coding. Remember to refresh your browser if changes don’t auto-reload. Happy contributing! πŸŽ‰ - -## Missing a Feature? - -If a feature is missing, you can directly _request_ a new one [here](https://github.com/makeplane/plane/issues/new?assignees=&labels=feature&template=feature_request.yml&title=%F0%9F%9A%80+Feature%3A+). You also can do the same by choosing "πŸš€ Feature" when raising a [New Issue](https://github.com/makeplane/plane/issues/new/choose) on our GitHub Repository. -If you would like to _implement_ it, an issue with your proposal must be submitted first, to be sure that we can use it. Please consider the guidelines given below. - -## Coding guidelines - -To ensure consistency throughout the source code, please keep these rules in mind as you are working: - -- All features or bug fixes must be tested by one or more specs (unit-tests). -- We use [Eslint default rule guide](https://eslint.org/docs/rules/), with minor changes. An automated formatter is available using prettier. - -## Ways to contribute - -- Try Plane Cloud and the self hosting platform and give feedback -- Add new integrations -- Add or update translations -- Help with open [issues](https://github.com/makeplane/plane/issues) or [create your own](https://github.com/makeplane/plane/issues/new/choose) -- Share your thoughts and suggestions with us -- Help create tutorials and blog posts -- Request a feature by submitting a proposal -- Report a bug -- **Improve documentation** - fix incomplete or missing [docs](https://docs.plane.so/), bad wording, examples or explanations. - -## Contributing to language support - -This guide is designed to help contributors understand how to add or update translations in the application. - -### Understanding translation structure - -#### File organization - -Translations are organized by language in the locales directory. Each language has its own folder containing JSON files for translations. Here's how it looks: - -``` -packages/i18n/src/locales/ - β”œβ”€β”€ en/ - β”‚ β”œβ”€β”€ core.json # Critical translations - β”‚ └── translations.json - β”œβ”€β”€ fr/ - β”‚ └── translations.json - └── [language]/ - └── translations.json -``` - -#### Nested structure - -To keep translations organized, we use a nested structure for keys. This makes it easier to manage and locate specific translations. For example: - -```json -{ - "issue": { - "label": "Work item", - "title": { - "label": "Work item title" - } - } -} -``` - -### Translation formatting guide - -We use [IntlMessageFormat](https://formatjs.github.io/docs/intl-messageformat/) to handle dynamic content, such as variables and pluralization. Here's how to format your translations: - -#### Examples - -- **Simple variables** - - ```json - { - "greeting": "Hello, {name}!" - } - ``` - -- **Pluralization** - ```json - { - "items": "{count, plural, one {Work item} other {Work items}}" - } - ``` - -### Contributing guidelines - -#### Updating existing translations - -1. Locate the key in `locales//translations.json`. - -2. Update the value while ensuring the key structure remains intact. -3. Preserve any existing ICU formats (e.g., variables, pluralization). - -#### Adding new translation keys - -1. When introducing a new key, ensure it is added to **all** language files, even if translations are not immediately available. Use English as a placeholder if needed. - -2. Keep the nesting structure consistent across all languages. - -3. If the new key requires dynamic content (e.g., variables or pluralization), ensure the ICU format is applied uniformly across all languages. - -### Adding new languages - -Adding a new language involves several steps to ensure it integrates seamlessly with the project. Follow these instructions carefully: - -1. **Update type definitions** - Add the new language to the TLanguage type in the language definitions file: - -```ts - // packages/i18n/src/types/language.ts - export type TLanguage = "en" | "fr" | "your-lang"; -``` - -1. **Add language configuration** - Include the new language in the list of supported languages: -```ts - // packages/i18n/src/constants/language.ts - export const SUPPORTED_LANGUAGES: ILanguageOption[] = [ - { label: "English", value: "en" }, - { label: "Your Language", value: "your-lang" } - ]; -``` - -2. **Create translation files** - 1. Create a new folder for your language under locales (e.g., `locales/your-lang/`). - - 2. Add a `translations.json` file inside the folder. - - 3. Copy the structure from an existing translation file and translate all keys. - -3. **Update import logic** - Modify the language import logic to include your new language: -```ts - private importLanguageFile(language: TLanguage): Promise { - switch (language) { - case "your-lang": - return import("../locales/your-lang/translations.json"); - // ... - } - } -``` - -### Quality checklist - -Before submitting your contribution, please ensure the following: - -- All translation keys exist in every language file. -- Nested structures match across all language files. -- ICU message formats are correctly implemented. -- All languages load without errors in the application. -- Dynamic values and pluralization work as expected. -- There are no missing or untranslated keys. - -#### Pro tips - -- When in doubt, refer to the English translations for context. -- Verify pluralization works with different numbers. -- Ensure dynamic values (e.g., `{name}`) are correctly interpolated. -- Double-check that nested key access paths are accurate. - -Happy translating! 🌍✨ - -## Need help? Questions and suggestions - -Questions, suggestions, and thoughts are most welcome. We can also be reached in our [Discord Server](https://discord.com/invite/A92xrEGCge). diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 0e11bbb5570..00000000000 --- a/SECURITY.md +++ /dev/null @@ -1,39 +0,0 @@ -# Security policy -This document outlines the security protocols and vulnerability reporting guidelines for the Plane project. Ensuring the security of our systems is a top priority, and while we work diligently to maintain robust protection, vulnerabilities may still occur. We highly value the community’s role in identifying and reporting security concerns to uphold the integrity of our systems and safeguard our users. - -## Reporting a vulnerability -If you have identified a security vulnerability, submit your findings to [security@plane.so](mailto:security@plane.so). -Ensure your report includes all relevant information needed for us to reproduce and assess the issue. Include the IP address or URL of the affected system. - -To ensure a responsible and effective disclosure process, please adhere to the following: - -- Maintain confidentiality and refrain from publicly disclosing the vulnerability until we have had the opportunity to investigate and address the issue. -- Refrain from running automated vulnerability scans on our infrastructure or dashboard without prior consent. Contact us to set up a sandbox environment if necessary. -- Do not exploit any discovered vulnerabilities for malicious purposes, such as accessing or altering user data. -- Do not engage in physical security attacks, social engineering, distributed denial of service (DDoS) attacks, spam campaigns, or attacks on third-party applications as part of your vulnerability testing. - -## Out of scope -While we appreciate all efforts to assist in improving our security, please note that the following types of vulnerabilities are considered out of scope: - -- Vulnerabilities requiring man-in-the-middle (MITM) attacks or physical access to a user’s device. -- Content spoofing or text injection issues without a clear attack vector or the ability to modify HTML/CSS. -- Issues related to email spoofing. -- Missing DNSSEC, CAA, or CSP headers. -- Absence of secure or HTTP-only flags on non-sensitive cookies. - -## Our commitment - -At Plane, we are committed to maintaining transparent and collaborative communication throughout the vulnerability resolution process. Here's what you can expect from us: - -- **Response Time**
-We will acknowledge receipt of your vulnerability report within three business days and provide an estimated timeline for resolution. -- **Legal Protection**
-We will not initiate legal action against you for reporting vulnerabilities, provided you adhere to the reporting guidelines. -- **Confidentiality**
-Your report will be treated with confidentiality. We will not disclose your personal information to third parties without your consent. -- **Recognition**
-With your permission, we are happy to publicly acknowledge your contribution to improving our security once the issue is resolved. -- **Timely Resolution**
-We are committed to working closely with you throughout the resolution process, providing timely updates as necessary. Our goal is to address all reported vulnerabilities swiftly, and we will actively engage with you to coordinate a responsible disclosure once the issue is fully resolved. - -We appreciate your help in ensuring the security of our platform. Your contributions are crucial to protecting our users and maintaining a secure environment. Thank you for working with us to keep Plane safe. \ No newline at end of file diff --git a/deployments/aio/community/Dockerfile b/deployments/aio/community/Dockerfile deleted file mode 100644 index b217d09f8bb..00000000000 --- a/deployments/aio/community/Dockerfile +++ /dev/null @@ -1,68 +0,0 @@ -ARG PLANE_VERSION=v0.27.1 -FROM --platform=$BUILDPLATFORM tonistiigi/binfmt AS binfmt - -# ************************************************** -# STAGE 0: Image Loading -# ************************************************** -FROM node:22-alpine AS node - -FROM artifacts.plane.so/makeplane/plane-frontend:${PLANE_VERSION} AS web-img -FROM artifacts.plane.so/makeplane/plane-backend:${PLANE_VERSION} AS backend-img -FROM artifacts.plane.so/makeplane/plane-space:${PLANE_VERSION} AS space-img -FROM artifacts.plane.so/makeplane/plane-admin:${PLANE_VERSION} AS admin-img -FROM artifacts.plane.so/makeplane/plane-live:${PLANE_VERSION} AS live-img -FROM artifacts.plane.so/makeplane/plane-proxy:${PLANE_VERSION} AS proxy-img - -# ************************************************** -# STAGE 1: Runner -# ************************************************** -FROM python:3.12.10-alpine AS runner - -WORKDIR /app - -RUN apk add --no-cache \ - "libpq" \ - "libxslt" \ - "xmlsec" - - -COPY --from=node /usr/lib /usr/lib -COPY --from=node /usr/local/lib /usr/local/lib -COPY --from=node /usr/local/include /usr/local/include -COPY --from=node /usr/local/bin /usr/local/bin - -COPY --from=web-img /app /app/web -COPY --from=space-img /app /app/space -COPY --from=admin-img /app /app/admin -COPY --from=live-img /app /app/live - -RUN rm -rf /app/web/apps/web/.next/cache && \ - rm -rf /app/space/apps/space/.next/cache && \ - rm -rf /app/admin/apps/admin/.next/cache - -COPY --from=proxy-img /usr/bin/caddy /usr/bin/caddy -COPY dist/Caddyfile /app/proxy/Caddyfile - -COPY --from=backend-img /code /app/backend -COPY --from=backend-img /usr/local/lib/python3.12/site-packages/ /usr/local/lib/python3.12/site-packages/ -COPY --from=backend-img /usr/local/bin/ /usr/local/bin/ - -RUN apk add --no-cache nss-tools bash curl uuidgen ncdu vim - -RUN pip install supervisor -RUN mkdir -p /etc/supervisor/conf.d - -COPY start.sh /app/start.sh -COPY dist/plane.env /app/plane.env -COPY supervisor.conf /etc/supervisor/conf.d/supervisor.conf - -RUN mkdir -p /app/logs/access && \ - mkdir -p /app/logs/error && \ - mkdir -p /app/data && \ - chmod +x /app/start.sh - -VOLUME ['/app/data', '/app/logs'] - -EXPOSE 80 443 - -CMD ["/app/start.sh"] diff --git a/deployments/aio/community/README.md b/deployments/aio/community/README.md deleted file mode 100644 index 96aab6737d4..00000000000 --- a/deployments/aio/community/README.md +++ /dev/null @@ -1,174 +0,0 @@ -# Plane Community All-In-One (AIO) Docker Image - -The Plane Community All-In-One Docker image packages all Plane services into a single container for easy deployment and testing. This image includes web interface, API server, background workers, live server, and more. - -## What's Included - -The AIO image contains the following services: - -- **Web App** (Port 3001): Main Plane web interface -- **Space** (Port 3002): Public project spaces -- **Admin** (Port 3003): Administrative interface -- **API Server** (Port 3004): Backend API -- **Live Server** (Port 3005): Real-time collaboration -- **Proxy** (Port 80, 443): Caddy reverse proxy -- **Worker & Beat**: Background task processing - -## Prerequisites - -### Required External Services - -The AIO image requires these external services to be running: - -- **PostgreSQL Database**: For data storage -- **Redis**: For caching and session management -- **RabbitMQ**: For message queuing -- **S3-Compatible Storage**: For file uploads (AWS S3 or MinIO) - -### Required Environment Variables - -You must provide these environment variables: - -#### Core Configuration - -- `DOMAIN_NAME`: Your domain name or IP address -- `DATABASE_URL`: PostgreSQL connection string -- `REDIS_URL`: Redis connection string -- `AMQP_URL`: RabbitMQ connection string - -#### Storage Configuration - -- `AWS_REGION`: AWS region (e.g., us-east-1) -- `AWS_ACCESS_KEY_ID`: S3 access key -- `AWS_SECRET_ACCESS_KEY`: S3 secret key -- `AWS_S3_BUCKET_NAME`: S3 bucket name -- `AWS_S3_ENDPOINT_URL`: S3 endpoint (optional, defaults to AWS) - -## Quick Start - -### Basic Usage - -```bash -docker run --name plane-aio --rm -it \ - -p 80:80 \ - -e DOMAIN_NAME=your-domain.com \ - -e DATABASE_URL=postgresql://user:pass@host:port/database \ - -e REDIS_URL=redis://host:port \ - -e AMQP_URL=amqp://user:pass@host:port/vhost \ - -e AWS_REGION=us-east-1 \ - -e AWS_ACCESS_KEY_ID=your-access-key \ - -e AWS_SECRET_ACCESS_KEY=your-secret-key \ - -e AWS_S3_BUCKET_NAME=your-bucket \ - artifacts.plane.so/makeplane/plane-aio-community:latest -``` - -### Example with IP Address - -```bash -MYIP=192.168.68.169 -docker run --name myaio --rm -it \ - -p 80:80 \ - -e DOMAIN_NAME=${MYIP} \ - -e DATABASE_URL=postgresql://plane:plane@${MYIP}:15432/plane \ - -e REDIS_URL=redis://${MYIP}:16379 \ - -e AMQP_URL=amqp://plane:plane@${MYIP}:15673/plane \ - -e AWS_REGION=us-east-1 \ - -e AWS_ACCESS_KEY_ID=5MV45J9NF5TEFZWYCRAX \ - -e AWS_SECRET_ACCESS_KEY=7xMqAiAHsf2UUjMH+EwICXlyJL9TO30m8leEaDsL \ - -e AWS_S3_BUCKET_NAME=plane-app \ - -e AWS_S3_ENDPOINT_URL=http://${MYIP}:19000 \ - -e FILE_SIZE_LIMIT=10485760 \ - artifacts.plane.so/makeplane/plane-aio-community:latest -``` - -## Configuration Options - -### Optional Environment Variables - -#### Network & Protocol - -- `SITE_ADDRESS`: Server bind address (default: `:80`) - - -#### Security & Secrets - -- `SECRET_KEY`: Django secret key (default provided) -- `LIVE_SERVER_SECRET_KEY`: Live server secret (default provided) - -#### File Handling - -- `FILE_SIZE_LIMIT`: Maximum file upload size in bytes (default: `5242880` = 5MB) - -#### API Configuration - -- `API_KEY_RATE_LIMIT`: API key rate limit (default: `60/minute`) - -## Port Mapping - -The following ports are exposed: - -- `80`: Main web interface (HTTP) -- `443`: HTTPS (if SSL configured) - -## Volume Mounts - -### Recommended Persistent Volumes - -```bash --v /path/to/logs:/app/logs \ --v /path/to/data:/app/data -``` - -## Building the Image - -To build the AIO image yourself: - -```bash -cd deployments/aio/community -IMAGE_NAME=myplane-aio ./build.sh --release=v0.27.1 [--platform=linux/amd64] -``` - -Available build options: - -- `--release`: Plane version to build (required) -- `--image-name`: Custom image name (default: `plane-aio-community`) - -## Troubleshooting - -### Logs - -All service logs are available in `/app/logs/`: - -- Access logs: `/app/logs/access/` -- Error logs: `/app/logs/error/` - -### Health Checks - -The container runs multiple services managed by Supervisor. Check service status: - -```bash -docker exec -it supervisorctl status -``` - -### Common Issues - -1. **Database Connection Failed**: Ensure PostgreSQL is accessible and credentials are correct -2. **Redis Connection Failed**: Verify Redis server is running and URL is correct -3. **File Upload Issues**: Check S3 credentials and bucket permissions - -### Environment Validation - -The container will validate required environment variables on startup and display helpful error messages if any are missing. - -## Production Considerations - -- Use proper SSL certificates for HTTPS -- Configure proper backup strategies for data -- Monitor resource usage and scale accordingly -- Use external load balancer for high availability -- Regularly update to latest versions -- Secure your environment variables and secrets - -## Support - -For issues and support, please refer to the official Plane documentation. diff --git a/deployments/aio/community/build.sh b/deployments/aio/community/build.sh deleted file mode 100755 index e69ac9a50b3..00000000000 --- a/deployments/aio/community/build.sh +++ /dev/null @@ -1,155 +0,0 @@ -#!/bin/bash - -set -e - -DIST_DIR=${DIST_DIR:-./dist} -CPU_ARCH=$(uname -m) -IMAGE_NAME=${IMAGE_NAME:-makeplane/plane-aio-community} - - -# loop though all flags and set the variables -for arg in "$@"; do - case $arg in - --release) - APP_RELEASE_VERSION="$2" - shift - shift - ;; - --release=*) - APP_RELEASE_VERSION="${arg#*=}" - shift - ;; - --image-name) - IMAGE_NAME="$2" - shift - shift - ;; - --image-name=*) - IMAGE_NAME="${arg#*=}" - shift - ;; - esac -done - - -if [ -z "$APP_RELEASE_VERSION" ]; then - echo "" - echo "Usage: " - echo " ./build.sh [flags]" - echo "" - echo "Flags:" - echo " --release= required (e.g. v0.27.1)" - echo "" - echo "Example: ./build.sh --release=v0.27.1 --platform=linux/amd64" - exit 1 -fi - -# Install yq if not present -if ! command -v yq &> /dev/null; then - echo "Installing yq..." - sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_${CPU_ARCH} - sudo chmod +x /usr/local/bin/yq -fi - -cd $(dirname "$0") - -string_replace(){ - local file="$1" - local search="$2" - local replace="$3" - - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' "s|$search|$replace|g" "$file" - else - sed -i "s|$search|$replace|g" "$file" - fi -} -remove_line(){ - local file="$1" - local line="$2" - - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' '/'$line'/d' "$file" - else - sed -i '/'$line'/d' "$file" - fi -} - -update_env_file(){ - local file="$1" - local key="$2" - local value="$3" - - # if key is in file, replace it - if [[ "$OSTYPE" == "darwin"* ]]; then - sed -i '' 's|^'$key'=.*|'$key'='$value'|' "$file" - else - sed -i 's|^'$key'=.*|'$key'='$value'|' "$file" - fi - - # if key not in file, add it - if ! grep -q "^$key=" "$file"; then - echo "$key=$value" >> "$file" - fi -} - -build_dist_files(){ - cp ./variables.env $DIST_DIR/plane.env - cp ../../../apps/proxy/Caddyfile.ce $DIST_DIR/Caddyfile - - echo "" >> $DIST_DIR/plane.env - echo "" >> $DIST_DIR/plane.env - - # update the plane.env file with the APP_RELEASE_VERSION - update_env_file $DIST_DIR/plane.env "APP_RELEASE_VERSION" "$APP_RELEASE_VERSION" - update_env_file $DIST_DIR/plane.env "APP_RELEASE" "$APP_RELEASE_VERSION" - update_env_file $DIST_DIR/plane.env "APP_VERSION" "$APP_RELEASE_VERSION" - - update_env_file $DIST_DIR/plane.env "API_BASE_URL" "http://localhost:3004" - update_env_file $DIST_DIR/plane.env "SITE_ADDRESS" ":80" - - # remove this line containing `plane-minio:9000` - remove_line $DIST_DIR/Caddyfile "plane-minio:9000" "" - - # in caddyfile, update `reverse_proxy /spaces/* space:3000` to `reverse_proxy /spaces/* space:3002` - string_replace $DIST_DIR/Caddyfile "web:3000" "localhost:3001" - string_replace $DIST_DIR/Caddyfile "space:3000" "localhost:3002" - string_replace $DIST_DIR/Caddyfile "admin:3000" "localhost:3003" - string_replace $DIST_DIR/Caddyfile "api:8000" "localhost:3004" - string_replace $DIST_DIR/Caddyfile "live:3000" "localhost:3005" - - - # print docker build command - echo "------------------------------------------------" - echo "Run the following command to build the image:" - echo "------------------------------------------------" - echo "" - echo "docker build -t $IMAGE_NAME \\" - echo " -f $(pwd)/Dockerfile \\" - echo " --build-arg PLANE_VERSION=$APP_RELEASE_VERSION \\" - echo " $(pwd)" - echo "" - echo "------------------------------------------------" -} - - -main(){ - # check if the dist directory exists - echo "" - if [ -d "$DIST_DIR" ]; then - echo "Cleaning existing dist directory..." - rm -rf $DIST_DIR - fi - echo "Creating dist directory..." - mkdir -p $DIST_DIR - echo "" - - build_dist_files - if [ $? -ne 0 ]; then - echo "Error: Failed to build docker image" - exit 1 - fi -} - -main "$@" - diff --git a/deployments/aio/community/start.sh b/deployments/aio/community/start.sh deleted file mode 100644 index 6b8c81561d5..00000000000 --- a/deployments/aio/community/start.sh +++ /dev/null @@ -1,169 +0,0 @@ -#!/bin/bash -e - -print_header(){ - clear - echo "------------------------------------------------" - echo "Plane Community (All-In-One)" - echo "------------------------------------------------" - echo "" - echo "You are required to pass below environment variables to the script" - echo " DOMAIN_NAME, DATABASE_URL, REDIS_URL, AMQP_URL" - echo " AWS_REGION, AWS_ACCESS_KEY_ID" - echo " AWS_SECRET_ACCESS_KEY, AWS_S3_BUCKET_NAME" - echo "" - echo "Other optional environment variables: " - echo " SITE_ADDRESS (default: ':80')" - echo " FILE_SIZE_LIMIT (default: 5242880)" - echo " APP_PROTOCOL (http or https)" - echo " SECRET_KEY (default: 60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5)" - echo " LIVE_SERVER_SECRET_KEY (default: htbqvBJAgpm9bzvf3r4urJer0ENReatceh)" - echo "" - echo "" -} - -check_required_env(){ - echo "Checking required environment variables..." - local keys=("DOMAIN_NAME" "DATABASE_URL" "REDIS_URL" "AMQP_URL" - "AWS_REGION" "AWS_ACCESS_KEY_ID" "AWS_SECRET_ACCESS_KEY" "AWS_S3_BUCKET_NAME") - - local missing_keys=() - # Check if the environment variable is set and not empty - for key in "${keys[@]}"; do - if [ -z "${!key}" ]; then - echo " ❌ '$key' is not set or is empty" - missing_keys+=("$key") - fi - done - - if [ ${#missing_keys[@]} -gt 0 ]; then - echo "" - exit 1 - fi - # add checkmark - echo "βœ… Required environment variables are available" - echo "" -} - -update_env_value(){ - local key="$1" - local value="$2" - - # check if the file exists - if [ ! -f "plane.env" ]; then - echo "plane.env file not found" - exit 1 - fi - - # check if the key exists and add it if it doesn't - if ! grep -q "^$key=.*" plane.env; then - echo "${key}=${value}" >> plane.env - return 0 - fi - - # if key and value are not empty, update the value - if [ -n "$key" ] && [ -n "$value" ]; then - sed -i "s|^$key=.*|$key=$value|" plane.env - return 0 - fi - -} - -check_pre_requisites(){ - check_required_env - - # check if the file exists - if [ ! -f "plane.env" ]; then - echo "plane.env file not found" - exit 1 - fi - # add a new line to the end of the file - echo "" >> plane.env - echo "" >> plane.env - echo "βœ… Pre-requisites checked" - echo "" - -} - -validate_domain_name() { - local domain="$1" - - # Check if it's an IP address first - if [[ "$domain" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "IP" - return 0 - fi - - # FQDN validation regex - local fqdn_regex='^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*\.?$' - - if [[ "$domain" =~ $fqdn_regex ]]; then - # Additional checks - if [[ ${#domain} -le 253 ]] && [[ ! "$domain" =~ \.\. ]] && [[ ! "$domain" =~ ^- ]] && [[ ! "$domain" =~ -\. ]]; then - echo "FQDN" - return 0 - fi - fi - - echo "INVALID" - return 1 -} - -update_env_file(){ - echo "Updating environment file..." - # check if DOMAIN_NAME is valid IP address - local domain_type=$(validate_domain_name "$DOMAIN_NAME") - if [ "$domain_type" == "INVALID" ]; then - echo "DOMAIN_NAME is not a valid FQDN or IP address" - exit 1 - fi - - local app_protocol=${APP_PROTOCOL:-http} - - update_env_value "APP_PROTOCOL" "$app_protocol" - update_env_value "DOMAIN_NAME" "$DOMAIN_NAME" - update_env_value "APP_DOMAIN" "$DOMAIN_NAME" - if [ -n "$SITE_ADDRESS" ]; then - update_env_value "SITE_ADDRESS" "$SITE_ADDRESS" - else - update_env_value "SITE_ADDRESS" ":80" - fi - update_env_value "WEB_URL" "$app_protocol://$DOMAIN_NAME" - update_env_value "CORS_ALLOWED_ORIGINS" "http://$DOMAIN_NAME,https://$DOMAIN_NAME" - - # update database url - update_env_value "DATABASE_URL" "$DATABASE_URL" - update_env_value "REDIS_URL" "$REDIS_URL" - update_env_value "AMQP_URL" "$AMQP_URL" - - # update aws credentials - update_env_value "AWS_REGION" "$AWS_REGION" - update_env_value "AWS_ACCESS_KEY_ID" "$AWS_ACCESS_KEY_ID" - update_env_value "AWS_SECRET_ACCESS_KEY" "$AWS_SECRET_ACCESS_KEY" - update_env_value "AWS_S3_BUCKET_NAME" "$AWS_S3_BUCKET_NAME" - update_env_value "AWS_S3_ENDPOINT_URL" "${AWS_S3_ENDPOINT_URL:-https://s3.${AWS_REGION}.amazonaws.com}" - update_env_value "BUCKET_NAME" "$AWS_S3_BUCKET_NAME" - update_env_value "USE_MINIO" "0" - - # Optional environment variables - update_env_value "SECRET_KEY" "${SECRET_KEY:-60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5}" - update_env_value "FILE_SIZE_LIMIT" "${FILE_SIZE_LIMIT:-5242880}" - update_env_value "LIVE_SERVER_SECRET_KEY" "${LIVE_SERVER_SECRET_KEY:-htbqvBJAgpm9bzvf3r4urJer0ENReatceh}" - - update_env_value "API_KEY_RATE_LIMIT" "${API_KEY_RATE_LIMIT:-60/minute}" - - echo "βœ… Environment file updated" - echo "" -} - -main(){ - print_header - check_pre_requisites - update_env_file - - # load plane.env as exported variables - export $(grep -v '^#' plane.env | xargs) - - /usr/local/bin/supervisord -c /etc/supervisor/conf.d/supervisor.conf -} - -main "$@" \ No newline at end of file diff --git a/deployments/aio/community/supervisor.conf b/deployments/aio/community/supervisor.conf deleted file mode 100644 index 20a51f0c504..00000000000 --- a/deployments/aio/community/supervisor.conf +++ /dev/null @@ -1,124 +0,0 @@ -[supervisord] -user=root -nodaemon=true -stderr_logfile=/app/logs/error/supervisor.err.log -stdout_logfile=/app/logs/access/supervisor.log - -[program:migrator] -directory=/app/backend -command=sh -c "./bin/docker-entrypoint-migrator.sh" -autostart=true -autorestart=unexpected -stdout_logfile=/app/logs/access/migrator.log -stderr_logfile=/app/logs/error/migrator.err.log -# stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=50MB -stderr_logfile_backups=5 -priority=10 - - -[program:web] -command=sh -c "node /app/web/apps/web/server.js" -autostart=true -autorestart=true -stdout_logfile=/app/logs/access/web.log -stderr_logfile=/app/logs/error/web.err.log -# stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=50MB -stderr_logfile_backups=5 -environment=PORT=3001,HOSTNAME=0.0.0.0 -priority=15 - -[program:space] -command=sh -c "node /app/space/apps/space/server.js" -autostart=true -autorestart=true -stdout_logfile=/app/logs/access/space.log -stderr_logfile=/app/logs/error/space.err.log -# stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=50MB -stderr_logfile_backups=5 -environment=PORT=3002,HOSTNAME=0.0.0.0 -priority=15 - -[program:admin] -command=sh -c "node /app/admin/apps/admin/server.js" -autostart=true -autorestart=true -stdout_logfile=/app/logs/access/admin.log -stderr_logfile=/app/logs/error/admin.err.log -# stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=50MB -stderr_logfile_backups=5 -environment=PORT=3003,HOSTNAME=0.0.0.0 -priority=15 - -[program:api] -directory=/app/backend -command=sh -c "./bin/docker-entrypoint-api.sh" -autostart=true -autorestart=true -stdout_logfile=/app/logs/access/api.log -stdout_logfile_maxbytes=0 -stderr_logfile=/app/logs/error/api.err.log -# stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=50MB -stderr_logfile_backups=5 -environment=PORT=3004,HOSTNAME=0.0.0.0 -priority=15 - - -[program:worker] -directory=/app/backend -command=sh -c "./bin/docker-entrypoint-worker.sh" -autostart=true -autorestart=true -stdout_logfile=/app/logs/access/worker.log -stdout_logfile_maxbytes=0 -stderr_logfile=/app/logs/error/worker.err.log -# stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=50MB -stderr_logfile_backups=5 -priority=20 - -[program:beat] -directory=/app/backend -command=sh -c "./bin/docker-entrypoint-beat.sh" -autostart=true -autorestart=true -stdout_logfile=/app/logs/access/beat.log -stdout_logfile_maxbytes=0 -stderr_logfile=/app/logs/error/beat.err.log -# stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=50MB -stderr_logfile_backups=5 -priority=20 - - -[program:live] -directory=/app/live -command=sh -c "node live/server.js" -autostart=true -autorestart=true -stdout_logfile=/app/logs/access/live.log -stdout_logfile_maxbytes=0 -stderr_logfile=/app/logs/error/live.err.log -# stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=50MB -stderr_logfile_backups=5 -environment=PORT=3005,HOSTNAME=0.0.0.0 -priority=20 - - -[program:proxy] -directory=/app/proxy -command=sh -c "caddy run --config /app/proxy/Caddyfile" -autostart=true -autorestart=true -stdout_logfile=/app/logs/access/proxy.log -stdout_logfile_maxbytes=0 -stderr_logfile=/app/logs/error/proxy.err.log -# stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=50MB -stderr_logfile_backups=5 -priority=20 \ No newline at end of file diff --git a/deployments/aio/community/variables.env b/deployments/aio/community/variables.env deleted file mode 100644 index 99d93e3fda2..00000000000 --- a/deployments/aio/community/variables.env +++ /dev/null @@ -1,53 +0,0 @@ -APP_DOMAIN=localhost -APP_RELEASE=stable - -# If SSL Cert to be generated, set CERT_EMAIl="email " -CERT_EMAIL= -CERT_ACME_CA=https://acme-v02.api.letsencrypt.org/directory - -SITE_ADDRESS=:80 - -# For DNS Challenge based certificate generation, set the CERT_ACME_DNS, CERT_EMAIL -# CERT_ACME_DNS="acme_dns " -CERT_ACME_DNS= - -WEB_URL=http://localhost -DEBUG=0 -CORS_ALLOWED_ORIGINS=http://localhost -API_BASE_URL=http://localhost:3004 - -#DB SETTINGS -DATABASE_URL= - -# REDIS SETTINGS -REDIS_URL= - -# RabbitMQ Settings -AMQP_URL= - -# Secret Key -SECRET_KEY=60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 - -# DATA STORE SETTINGS -USE_MINIO=0 -AWS_REGION= -AWS_ACCESS_KEY_ID= -AWS_SECRET_ACCESS_KEY= -AWS_S3_ENDPOINT_URL=https://s3.amazonaws.com -AWS_S3_BUCKET_NAME= -BUCKET_NAME= -FILE_SIZE_LIMIT=5242880 - -# Gunicorn Workers -GUNICORN_WORKERS=1 - - - -# Force HTTPS for handling SSL Termination -MINIO_ENDPOINT_SSL=0 - -# API key rate limit -API_KEY_RATE_LIMIT=60/minute - -# Live Server Secret Key -LIVE_SERVER_SECRET_KEY=htbqvBJAgpm9bzvf3r4urJer0ENReatceh diff --git a/deployments/cli/community/README.md b/deployments/cli/community/README.md deleted file mode 100644 index 8cec0f72b35..00000000000 --- a/deployments/cli/community/README.md +++ /dev/null @@ -1,630 +0,0 @@ -# Self Hosting - -In this guide, we will walk you through the process of setting up a self-hosted environment. Self-hosting allows you to have full control over your applications and data. It's a great way to ensure privacy, control, and customization. - -We will cover two main options for setting up your self-hosted environment: using a cloud server or using your desktop. For the cloud server, we will use an AWS EC2 instance. For the desktop, we will use Docker to create a local environment. - -Let's get started! - -## Setting up Docker Environment - -
- Option 1 - Using Cloud Server -

Best way to start is to create EC2 machine on AWS. It must have minimum of 2vCPU and 4GB RAM.

-

Run the below command to install docker engine.

- -`curl -fsSL https://get.docker.com | sh -` - -
- ---- - -
- Option 2 - Using Desktop - -#### For Mac - -
    -
  1. Download Docker Desktop for Mac from the Docker Hub.
  2. -
  3. Double-click the downloaded `.dmg` file and drag the Docker app icon to the Applications folder.
  4. -
  5. Open Docker Desktop from the Applications folder. You might be asked to provide your system password to install additional software.
  6. -
- -#### For Windows: - -
    -
  1. Download Docker Desktop for Windows from the Docker Hub.
  2. -
  3. Run the installer and follow the instructions. You might be asked to enable Hyper-V and "Containers" Windows features.
  4. -
  5. Open Docker Desktop. You might be asked to log out and log back in, or restart your machine, for changes to take effect.
  6. -
- -After installation, you can verify the installation by opening a terminal (Command Prompt on Windows, Terminal app on Mac) and running the command `docker --version`. This should display the installed version of Docker. - -
- ---- - -## Installing Plane - -Installing plane is a very easy and minimal step process. - -### Prerequisite - -- Docker installed and running -- OS with bash scripting enabled (Ubuntu, Linux AMI, macos). Windows systems need to have [gitbash](https://git-scm.com/download/win) -- User context used must have access to docker services. In most cases, use sudo su to switch as root user -- Use the terminal (or gitbash) window to run all the future steps - -### Downloading Latest Release - -``` -mkdir plane-selfhost - -cd plane-selfhost -``` - -#### For *Docker Compose* based setup - -``` -curl -fsSL -o setup.sh https://github.com/makeplane/plane/releases/latest/download/setup.sh - -chmod +x setup.sh -``` - -#### For *Docker Swarm* based setup - -``` -curl -fsSL -o setup.sh https://github.com/makeplane/plane/releases/latest/download/swarm.sh - -chmod +x setup.sh -``` - ---- - -### Proceed with setup - -Above steps will set you ready to install and start plane services. - -Lets get started by running the `./setup.sh` command. - -This will prompt you with the below options. - -#### Docker Compose -```bash -Select an Action you want to perform: - 1) Install (x86_64) - 2) Start - 3) Stop - 4) Restart - 5) Upgrade - 6) View Logs - 7) Backup Data - 8) Exit - -Action [2]: 1 -``` - -For the 1st time setup, type "1" as action input. - -This will create a folder `plane-app` and will download 2 files inside that - -- `docker-compose.yaml` -- `plane.env` - -Again the `options [1-8]` will be popped up, and this time hit `8` to exit. - -#### Docker Swarm - -```bash -Select an Action you want to perform: - 1) Deploy Stack - 2) Remove Stack - 3) View Stack Status - 4) Redeploy Stack - 5) Upgrade - 6) View Logs - 7) Exit - -Action [3]: 1 -``` - -For the 1st time setup, type "1" as action input. - -This will create a create a folder `plane-app` and will download 2 files inside that - -- `docker-compose.yaml` -- `plane.env` - -Again the `options [1-7]` will be popped up, and this time hit `7` to exit. - ---- - -### Continue with setup - Environment Settings - -Before proceeding, we suggest used to review `.env` file and set the values. -Below are the most import keys you must refer to. _You can use any text editor to edit this file_. - -> `LISTEN_HTTP_PORT` - This is default set to `80`. Make sure the port you choose to use is not preoccupied. (e.g `LISTEN_HTTP_PORT=8080`) - -> `WEB_URL` - This is default set to `http://localhost`. Change this to the FQDN you plan to use along with LISTEN_HTTP_PORT (eg. `https://plane.example.com:8080` or `http://[IP-ADDRESS]:8080`) - -> `CORS_ALLOWED_ORIGINS` - This is default set to `http://localhost`. Change this to the FQDN you plan to use along with LISTEN_HTTP_PORT (eg. `https://plane.example.com:8080` or `http://[IP-ADDRESS]:8080`) - -There are many other settings you can play with, but we suggest you configure `EMAIL SETTINGS` as it will enable you to invite your teammates onto the platform. - ---- - -### Continue with setup - Start Server (Docker Compose) - -Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `2` to start the sevices - -```bash -Select a Action you want to perform: - 1) Install (x86_64) - 2) Start - 3) Stop - 4) Restart - 5) Upgrade - 6) View Logs - 7) Backup Data - 8) Exit - -Action [2]: 2 -``` - -Expect something like this. -![Downloading docker images](images/download.png) - -Be patient as it might take sometime based on download speed and system configuration. If all goes well, you must see something like this - -![Downloading completed](images/started.png) - -This is the confirmation that all images were downloaded and the services are up & running. - -You have successfully self hosted `Plane` instance. Access the application by going to IP or domain you have configured it (e.g `https://plane.example.com:8080` or `http://[IP-ADDRESS]:8080`) - ---- - -### Stopping the Server / Remove Stack - -In case you want to make changes to `plane.env` variables, we suggest you to stop the services before doing that. - -#### Docker Compose - -Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `3` to stop the sevices - -```bash -Select a Action you want to perform: - 1) Install (x86_64) - 2) Start - 3) Stop - 4) Restart - 5) Upgrade - 6) View Logs - 7) Backup Data - 8) Exit - -Action [2]: 3 -``` - -If all goes well, you must see something like this - -![Stop Services](images/stopped.png) - -#### Docker Swarm - -Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `2` to stop the sevices - -```bash -Select an Action you want to perform: - 1) Deploy Stack - 2) Remove Stack - 3) View Stack Status - 4) Redeploy Stack - 5) Upgrade - 6) View Logs - 7) Exit - -Action [3]: 2 -``` - -If all goes well, you will see the confirmation from docker cli - ---- - -### Restarting the Server / Redeploy Stack - -In case you want to make changes to `plane.env` variables, without stopping the server or you noticed some abnormalies in services, you can restart the services with `RESTART` / `REDEPLOY` option. - -Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `4` to restart the sevices - -#### Docker Compose -```bash -Select a Action you want to perform: - 1) Install (x86_64) - 2) Start - 3) Stop - 4) Restart - 5) Upgrade - 6) View Logs - 7) Backup Data - 8) Exit - -Action [2]: 4 -``` - -If all goes well, you must see something like this - -![Restart Services](images/restart.png) - -#### Docker Swarm - -```bash - 1) Deploy Stack - 2) Remove Stack - 3) View Stack Status - 4) Redeploy Stack - 5) Upgrade - 6) View Logs - 7) Exit - -Action [3]: 4 -``` - -If all goes well, you will see the confirmation from docker cli - ---- - -### Upgrading Plane Version - -It is always advised to keep Plane up to date with the latest release. - -Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `5` to upgrade the release. - -#### Docker Compose - -```bash -Select a Action you want to perform: - 1) Install (x86_64) - 2) Start - 3) Stop - 4) Restart - 5) Upgrade - 6) View Logs - 7) Backup Data - 8) Exit - -Action [2]: 5 -``` - -By choosing this, it will stop the services and then will download the latest `docker-compose.yaml` and `plane.env`. - -You must expect the below message - -![Alt text](images/upgrade.png) - -Once done, choose `8` to exit from prompt. - -> It is very important for you to validate the `plane.env` for the new changes. - -Once done with making changes in `plane.env` file, jump on to `Start Server` - -#### Docker Swarm - -Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `5` to upgrade the release. - -```bash - 1) Deploy Stack - 2) Remove Stack - 3) View Stack Status - 4) Redeploy Stack - 5) Upgrade - 6) View Logs - 7) Exit - -Action [3]: 5 -``` - -By choosing this, it will stop the services and then will download the latest `docker-compose.yaml` and `plane.env`. - -Once done, choose `7` to exit from prompt. - -> It is very important for you to validate the `plane.env` for the new changes. - -Once done with making changes in `plane.env` file, jump on to `Redeploy Stack` - ---- - -### View Logs - -There would a time when you might want to check what is happening inside the API, Worker or any other container. - -Lets again run the `./setup.sh` command. You will again be prompted with the below options. - -This time select `6` to view logs. - -#### Docker Compose - -```bash -Select a Action you want to perform: - 1) Install (x86_64) - 2) Start - 3) Stop - 4) Restart - 5) Upgrade - 6) View Logs - 7) Backup Data - 8) Exit - -Action [2]: 6 -``` - -#### Docker Swarm - - -```bash - 1) Deploy Stack - 2) Remove Stack - 3) View Stack Status - 4) Redeploy Stack - 5) Upgrade - 6) View Logs - 7) Exit - -Action [3]: 6 -``` - -#### Service Menu Options for Logs -This will further open sub-menu with list of services -```bash -Select a Service you want to view the logs for: - 1) Web - 2) Space - 3) API - 4) Worker - 5) Beat-Worker - 6) Migrator - 7) Proxy - 8) Redis - 9) Postgres - 10) Minio - 11) RabbitMQ - 0) Back to Main Menu - -Service: 3 -``` - -Select any of the service to view the logs e.g. `3`. Expect something similar to this -```bash -api-1 | Waiting for database... -api-1 | Database available! -api-1 | Waiting for database migrations to complete... -api-1 | Waiting for database migrations to complete... -api-1 | Waiting for database migrations to complete... -api-1 | Waiting for database migrations to complete... -api-1 | Waiting for database migrations to complete... -api-1 | Waiting for database migrations to complete... -api-1 | Waiting for database migrations to complete... -api-1 | No migrations Pending. Starting processes ... -api-1 | Instance registered -api-1 | ENABLE_SIGNUP loaded with value from environment variable. -api-1 | ENABLE_EMAIL_PASSWORD loaded with value from environment variable. -api-1 | ENABLE_MAGIC_LINK_LOGIN loaded with value from environment variable. -api-1 | GOOGLE_CLIENT_ID loaded with value from environment variable. -api-1 | GITHUB_CLIENT_ID loaded with value from environment variable. -api-1 | GITHUB_CLIENT_SECRET loaded with value from environment variable. -api-1 | EMAIL_HOST loaded with value from environment variable. -api-1 | EMAIL_HOST_USER loaded with value from environment variable. -api-1 | EMAIL_HOST_PASSWORD loaded with value from environment variable. -api-1 | EMAIL_PORT loaded with value from environment variable. -api-1 | EMAIL_FROM loaded with value from environment variable. -api-1 | EMAIL_USE_TLS loaded with value from environment variable. -api-1 | EMAIL_USE_SSL loaded with value from environment variable. -api-1 | OPENAI_API_KEY loaded with value from environment variable. -api-1 | GPT_ENGINE loaded with value from environment variable. -api-1 | UNSPLASH_ACCESS_KEY loaded with value from environment variable. -api-1 | Checking bucket... -api-1 | Bucket 'uploads' does not exist. Creating bucket... -api-1 | Bucket 'uploads' created successfully. -api-1 | Public read access policy set for bucket 'uploads'. -api-1 | Cache Cleared -api-1 | [2024-05-02 03:56:01 +0000] [1] [INFO] Starting gunicorn 21.2.0 -api-1 | [2024-05-02 03:56:01 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) -api-1 | [2024-05-02 03:56:01 +0000] [1] [INFO] Using worker: uvicorn.workers.UvicornWorker -api-1 | [2024-05-02 03:56:01 +0000] [25] [INFO] Booting worker with pid: 25 -api-1 | [2024-05-02 03:56:03 +0000] [25] [INFO] Started server process [25] -api-1 | [2024-05-02 03:56:03 +0000] [25] [INFO] Waiting for application startup. -api-1 | [2024-05-02 03:56:03 +0000] [25] [INFO] ASGI 'lifespan' protocol appears unsupported. -api-1 | [2024-05-02 03:56:03 +0000] [25] [INFO] Application startup complete. - -``` - -To exit this, use `CTRL+C` and then you will land on to the main-menu with the list of actions. - -Similarly, you can view the logs of other services. - ---- - -### Backup Data (Docker Compose) - -There would a time when you might want to backup your data from docker volumes to external storage like S3 or drives. - -Lets again run the `./setup.sh` command. You will again be prompted with the below options. This time select `7` to Backup the data. - -```bash -Select a Action you want to perform: - 1) Install (x86_64) - 2) Start - 3) Stop - 4) Restart - 5) Upgrade - 6) View Logs - 7) Backup Data - 8) Exit - -Action [2]: 7 -``` - -In response, you can find the backup folder - -```bash -Backing Up plane-app_pgdata -Backing Up plane-app_redisdata -Backing Up plane-app_uploads - -Backup completed successfully. Backup files are stored in /....../plane-app/backup/20240502-1120 -``` - ---- - -### Restore Data (Docker Compose) - -When you want to restore the previously backed-up data, follow the instructions below. - -1. Make sure that Plane-CE is installed, started, and then stopped. This ensures that the Docker volumes are created. - -1. Download the restore script using the command below. We suggest downloading it in the same folder as `setup.sh`. - - ```bash - curl -fsSL -o restore.sh https://github.com/makeplane/plane/releases/latest/download/restore.sh - chmod +x restore.sh - ``` - -1. Execute the command below to restore your data. - - ```bash - ./restore.sh - ``` - - As an example, for a backup folder `/opt/plane-selfhost/plane-app/backup/20240722-0914`, expect the response below: - - ```bash - -------------------------------------------- - ____ _ ///////// - | _ \| | __ _ _ __ ___ ///////// - | |_) | |/ _` | '_ \ / _ \ ///// ///// - | __/| | (_| | | | | __/ ///// ///// - |_| |_|\__,_|_| |_|\___| //// - //// - -------------------------------------------- - Project management tool from the future - -------------------------------------------- - Found /opt/plane-selfhost/plane-app/backup/20240722-0914/pgdata.tar.gz - .....Restoring plane-app_pgdata - .....Successfully restored volume plane-app_pgdata from pgdata.tar.gz - - Found /opt/plane-selfhost/plane-app/backup/20240722-0914/redisdata.tar.gz - .....Restoring plane-app_redisdata - .....Successfully restored volume plane-app_redisdata from redisdata.tar.gz - - Found /opt/plane-selfhost/plane-app/backup/20240722-0914/uploads.tar.gz - .....Restoring plane-app_uploads - .....Successfully restored volume plane-app_uploads from uploads.tar.gz - - - Restore completed successfully. - ``` - -1. Start the Plane instance using `./setup.sh start`. - ---- - -### Restore for Commercial Air-Gapped (Docker Compose) - -When you want to restore the previously backed-up data on Plane Commercial Air-Gapped version, follow the instructions below. - -1. Download the restore script using the command below - - ```bash - curl -fsSL -o restore-airgapped.sh https://github.com/makeplane/plane/releases/latest/download/restore-airgapped.sh - chmod +x restore-airgapped.sh - ``` - -1. Copy the backup folder and the `restore-airgapped.sh` to `Commercial Airgapped Edition` server - -1. Make sure that Plane Commercial (Airgapped) is extracted and ready to get started. In case it is running, you would need to stop that. - -1. Execute the command below to restore your data. - - ```bash - ./restore-airgapped.sh - ``` - -1. After restoration, you are ready to start Plane Commercial (Airgapped) will all your previously saved data. - ---- - -
-

Upgrading from v0.13.2 to v0.14.x

- -This is one time activity for users who are upgrading from v0.13.2 to v0.14.0 - -As there has been significant changes to Self Hosting process, this step mainly covers the data migration from current (v0.13.2) docker volumes from newly created volumes - -> Before we begin with migration, make sure your v0.14.0 was started and then stopped. This is required to know the newly created docker volume names. - -Begin with downloading the migration script using below command - -``` - -curl -fsSL -o migrate.sh https://raw.githubusercontent.com/makeplane/plane/master/deploy/selfhost/migration-0.13-0.14.sh - -chmod +x migrate.sh - -``` - -Now run the `./migrate.sh` command and expect the instructions as below - -``` -****************************************************************** - -This script is solely for the migration purpose only. -This is a 1 time migration of volume data from v0.13.2 => v0.14.x - -Assumption: -1. Postgres data volume name ends with _pgdata -2. Minio data volume name ends with _uploads -3. Redis data volume name ends with _redisdata - -Any changes to this script can break the migration. - -Before you proceed, make sure you run the below command -to know the docker volumes - -docker volume ls -q | grep -i "_pgdata" -docker volume ls -q | grep -i "_uploads" -docker volume ls -q | grep -i "_redisdata" - -******************************************************* - -Given below list of REDIS volumes, identify the prefix of source and destination volumes leaving "_redisdata" ---------------------- -plane-app_redisdata -v0132_redisdata - -Provide the Source Volume Prefix : -``` - -**Open another terminal window**, and run the mentioned 3 command. This may be different for users who have changed the volume names in their previous setup (v0.13.2) - -For every command you must see 2 records something like shown in above example of `redisdata` - -To move forward, you would need PREFIX of old setup and new setup. As per above example, `v0132` is the prefix of v0.13.2 and `plane-app` is the prefix of v0.14.0 setup - -**Back to original terminal window**, _Provide the Source Volume Prefix_ and hit ENTER. - -Now you will be prompted to _Provide Destination Volume Prefix_. Provide the value and hit ENTER - -``` -Provide the Source Volume Prefix : v0132 -Provide the Destination Volume Prefix : plane-app -``` - -In case the suffixes are wrong or the mentioned volumes are not found, you will receive the error shown below. The image below displays an error for source volumes. - -![Migrate Error](images/migrate-error.png) - -In case of successful migration, it will be a silent exit without error. - -Now its time to restart v0.14.0 setup. -
\ No newline at end of file diff --git a/deployments/cli/community/build.yml b/deployments/cli/community/build.yml deleted file mode 100644 index f7fc6a54586..00000000000 --- a/deployments/cli/community/build.yml +++ /dev/null @@ -1,36 +0,0 @@ -services: - web: - image: ${DOCKERHUB_USER:-local}/plane-frontend:${APP_RELEASE:-latest} - build: - context: ../../ - dockerfile: apps/web/Dockerfile.web - - space: - image: ${DOCKERHUB_USER:-local}/plane-space:${APP_RELEASE:-latest} - build: - context: ../../ - dockerfile: apps/space/Dockerfile.space - - admin: - image: ${DOCKERHUB_USER:-local}/plane-admin:${APP_RELEASE:-latest} - build: - context: ../../ - dockerfile: apps/admin/Dockerfile.admin - - live: - image: ${DOCKERHUB_USER:-local}/plane-live:${APP_RELEASE:-latest} - build: - context: ../../ - dockerfile: apps/live/Dockerfile.live - - api: - image: ${DOCKERHUB_USER:-local}/plane-backend:${APP_RELEASE:-latest} - build: - context: ../../apps/api - dockerfile: Dockerfile.api - - proxy: - image: ${DOCKERHUB_USER:-local}/plane-proxy:${APP_RELEASE:-latest} - build: - context: ../../apps/proxy - dockerfile: Dockerfile.ce diff --git a/deployments/cli/community/docker-compose.yml b/deployments/cli/community/docker-compose.yml deleted file mode 100644 index 7afc8d2a14f..00000000000 --- a/deployments/cli/community/docker-compose.yml +++ /dev/null @@ -1,254 +0,0 @@ -x-db-env: &db-env - PGHOST: ${PGHOST:-plane-db} - PGDATABASE: ${PGDATABASE:-plane} - POSTGRES_USER: ${POSTGRES_USER:-plane} - POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-plane} - POSTGRES_DB: ${POSTGRES_DB:-plane} - POSTGRES_PORT: ${POSTGRES_PORT:-5432} - PGDATA: ${PGDATA:-/var/lib/postgresql/data} - -x-redis-env: &redis-env - REDIS_HOST: ${REDIS_HOST:-plane-redis} - REDIS_PORT: ${REDIS_PORT:-6379} - REDIS_URL: ${REDIS_URL:-redis://plane-redis:6379/} - -x-minio-env: &minio-env - MINIO_ROOT_USER: ${AWS_ACCESS_KEY_ID:-access-key} - MINIO_ROOT_PASSWORD: ${AWS_SECRET_ACCESS_KEY:-secret-key} - -x-aws-s3-env: &aws-s3-env - AWS_REGION: ${AWS_REGION:-} - AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-access-key} - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-secret-key} - AWS_S3_ENDPOINT_URL: ${AWS_S3_ENDPOINT_URL:-http://plane-minio:9000} - AWS_S3_BUCKET_NAME: ${AWS_S3_BUCKET_NAME:-uploads} - -x-proxy-env: &proxy-env - SSL: ${SSL:-false} - APP_DOMAIN: ${APP_DOMAIN:-localhost} - FILE_SIZE_LIMIT: ${FILE_SIZE_LIMIT:-5242880} - CERT_EMAIL: ${CERT_EMAIL} - CERT_ACME_CA: ${CERT_ACME_CA} - CERT_ACME_DNS: ${CERT_ACME_DNS} - LISTEN_HTTP_PORT: ${LISTEN_HTTP_PORT:-80} - LISTEN_HTTPS_PORT: ${LISTEN_HTTPS_PORT:-443} - BUCKET_NAME: ${AWS_S3_BUCKET_NAME:-uploads} - SITE_ADDRESS: ${SITE_ADDRESS:-:80} - -x-mq-env: &mq-env # RabbitMQ Settings - RABBITMQ_HOST: ${RABBITMQ_HOST:-plane-mq} - RABBITMQ_PORT: ${RABBITMQ_PORT:-5672} - RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-plane} - RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:-plane} - RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_VHOST:-plane} - RABBITMQ_VHOST: ${RABBITMQ_VHOST:-plane} - -x-live-env: &live-env - API_BASE_URL: ${API_BASE_URL:-http://api:8000} - -x-app-env: &app-env - WEB_URL: ${WEB_URL:-http://localhost} - DEBUG: ${DEBUG:-0} - CORS_ALLOWED_ORIGINS: ${CORS_ALLOWED_ORIGINS} - GUNICORN_WORKERS: 1 - USE_MINIO: ${USE_MINIO:-1} - DATABASE_URL: ${DATABASE_URL:-postgresql://plane:plane@plane-db/plane} - SECRET_KEY: ${SECRET_KEY:-60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5} - AMQP_URL: ${AMQP_URL:-amqp://plane:plane@plane-mq:5672/plane} - API_KEY_RATE_LIMIT: ${API_KEY_RATE_LIMIT:-60/minute} - MINIO_ENDPOINT_SSL: ${MINIO_ENDPOINT_SSL:-0} - -services: - web: - image: artifacts.plane.so/makeplane/plane-frontend:${APP_RELEASE:-stable} - deploy: - replicas: ${WEB_REPLICAS:-1} - restart_policy: - condition: on-failure - depends_on: - - api - - worker - - space: - image: artifacts.plane.so/makeplane/plane-space:${APP_RELEASE:-stable} - deploy: - replicas: ${SPACE_REPLICAS:-1} - restart_policy: - condition: on-failure - depends_on: - - api - - worker - - web - - admin: - image: artifacts.plane.so/makeplane/plane-admin:${APP_RELEASE:-stable} - deploy: - replicas: ${ADMIN_REPLICAS:-1} - restart_policy: - condition: on-failure - depends_on: - - api - - web - - live: - image: artifacts.plane.so/makeplane/plane-live:${APP_RELEASE:-stable} - environment: - <<: [*live-env] - deploy: - replicas: ${LIVE_REPLICAS:-1} - restart_policy: - condition: on-failure - depends_on: - - api - - web - - api: - image: artifacts.plane.so/makeplane/plane-backend:${APP_RELEASE:-stable} - command: ./bin/docker-entrypoint-api.sh - deploy: - replicas: ${API_REPLICAS:-1} - restart_policy: - condition: on-failure - volumes: - - logs_api:/code/plane/logs - environment: - <<: [*app-env, *db-env, *redis-env, *minio-env, *aws-s3-env, *proxy-env] - depends_on: - - plane-db - - plane-redis - - plane-mq - - worker: - image: artifacts.plane.so/makeplane/plane-backend:${APP_RELEASE:-stable} - command: ./bin/docker-entrypoint-worker.sh - deploy: - replicas: ${WORKER_REPLICAS:-1} - restart_policy: - condition: on-failure - volumes: - - logs_worker:/code/plane/logs - environment: - <<: [*app-env, *db-env, *redis-env, *minio-env, *aws-s3-env, *proxy-env] - depends_on: - - api - - plane-db - - plane-redis - - plane-mq - - beat-worker: - image: artifacts.plane.so/makeplane/plane-backend:${APP_RELEASE:-stable} - command: ./bin/docker-entrypoint-beat.sh - deploy: - replicas: ${BEAT_WORKER_REPLICAS:-1} - restart_policy: - condition: on-failure - volumes: - - logs_beat-worker:/code/plane/logs - environment: - <<: [*app-env, *db-env, *redis-env, *minio-env, *aws-s3-env, *proxy-env] - depends_on: - - api - - plane-db - - plane-redis - - plane-mq - - migrator: - image: artifacts.plane.so/makeplane/plane-backend:${APP_RELEASE:-stable} - command: ./bin/docker-entrypoint-migrator.sh - deploy: - replicas: 1 - restart_policy: - condition: on-failure - volumes: - - logs_migrator:/code/plane/logs - environment: - <<: [*app-env, *db-env, *redis-env, *minio-env, *aws-s3-env, *proxy-env] - depends_on: - - plane-db - - plane-redis - - # Comment this if you already have a database running - plane-db: - image: postgres:15.7-alpine - command: postgres -c 'max_connections=1000' - deploy: - replicas: 1 - restart_policy: - condition: on-failure - environment: - <<: *db-env - volumes: - - pgdata:/var/lib/postgresql/data - - plane-redis: - image: valkey/valkey:7.2.5-alpine - deploy: - replicas: 1 - restart_policy: - condition: on-failure - volumes: - - redisdata:/data - - plane-mq: - image: rabbitmq:3.13.6-management-alpine - deploy: - replicas: 1 - restart_policy: - condition: on-failure - environment: - <<: *mq-env - volumes: - - rabbitmq_data:/var/lib/rabbitmq - - # Comment this if you using any external s3 compatible storage - plane-minio: - image: minio/minio:latest - command: server /export --console-address ":9090" - deploy: - replicas: 1 - restart_policy: - condition: on-failure - environment: - <<: *minio-env - volumes: - - uploads:/export - - # Comment this if you already have a reverse proxy running - proxy: - image: artifacts.plane.so/makeplane/plane-proxy:${APP_RELEASE:-stable} - deploy: - replicas: 1 - restart_policy: - condition: on-failure - environment: - <<: *proxy-env - ports: - - target: 80 - published: ${LISTEN_HTTP_PORT:-80} - protocol: tcp - mode: host - - target: 443 - published: ${LISTEN_HTTPS_PORT:-443} - protocol: tcp - mode: host - volumes: - - proxy_config:/config - - proxy_data:/data - depends_on: - - web - - api - - space - - admin - - live - -volumes: - pgdata: - redisdata: - uploads: - logs_api: - logs_worker: - logs_beat-worker: - logs_migrator: - rabbitmq_data: - proxy_config: - proxy_data: diff --git a/deployments/cli/community/images/download.png b/deployments/cli/community/images/download.png deleted file mode 100644 index bb0d1183e68a8d455ce7959b6689bb97da679ab0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22985 zcmZVm1z6q8);2u!i`|a!6 zBs}SD10e_K?6+1ydK-DZnMHQq( zMTry~?MyAKO+Y}XzPWsrmjN?C8YFD~NJ`G z!?juDP}I2$QQ-#tW~ox9I56;VqcU(RiA=6d&VM(V_aZ%$8vgZq?=Wyn^;^(~MRe zsjU8kM))E+Q3eQ)n@9&|$YBP!=>6R4E5r+wVW@H(}I7X$=6J`@BR*rEVmu^h0!XTj)l!2j-pWW8S~tSl-e1#Fd#98FAYoy_f= z>#BRlfvy%TR5YA5WIyv5+1W4{7~2_|Fu2>;zc&HlbLRneZA_dEh}>u%^v@;ER{SIyvI<0^c8(@Q91KhhOe6wuL_|be`uN@5cKZ4Uh6Coy++ zw&!7FbaQiKaARe#b2MXQ=H}*RWMW}tVW9`Epm*}HbvAIPw{;@@PbYu-5i@ZzaQ7M*n{PGfoqCi~rrp*6F{G1w0_*`x!=N1}4UT`v#iwy&vUK zuy8lA))cd_0mK8`Lx7o`i|?Q7|DQAeyW{_9sqw#-?Cc!>x8?si^Z(yc)yc$B)XoOD zrL(~QX6C<*|L@8FHsoV`fAaq$iT{ZCpQC`B1>pD?|D7`dIJC<}SP&2)5GgTX6?f3H zFVHDy0}DUuzd=G^B()fS&G8R_j_hS5ii8zbR1_kE%Oim!;Zp#GB_ZhniHBs?2NU*? zhMG-yKtf0VHV)Q{g6SHN*(DdlzPh?PJ3GtDyISvcem~({QnspU=2>R9S=KhB+GMTC zS<3_#4MN%t^Ur2WA0iJ=hqDUj?}7K@D4-k~4-)ziSfCRA$nTpW(r&1z9Y2yPqW@i1 z)6;wZZ#UgATm(QPyf;(_NbJ9@B>X>ObVC2ziA47$Oij)0*Q_1NOIhcPUK2MyZCkH(%wK_N1cEsfOgdK976JSKvY992^Tu zsVCK4Sx_Hw$gbPgZF%gsKzyE#iW^KPaAfv^Mw~Z&?vV)|a5=1DBw{w}2z?(6IQU*) zpY1$vrX}kWt)}xJ`5$)^8f;cMa0I;Fo7nJFN)!S%+;=|Wu$sc#thIbwX|$5O-}DnC zCnH0$Pv>=Gpx3M;ljIiY`6~f27O*6E47Q7zLYdIB3dMY=m&b$bPzzVNSS;qiyu7>; z$vA?!X1mQAEIKBTU%!5xs&HrXNmlZWa`0`=+(Io{d69F zbXwRv&Lw<)Pd6547U#Z~zXnQuU!Lk>xFQVb^n=^7xSb>n_Z?dg?^f;ZleY2y&Loi_ z5mG}>yf#ru*zdcu)je{C_EVN#NvT|skP`Fhyep(ttE$$}{qa;$!M8uG>%fR<(#Lmb z&yw|gb8C%;P@)T1Oh(HU87N32wUo*S8ii;ASgT`p+bee;}! z(2uyI8BA(Kqg3(GzMUIB_g^e(y6pp))e1f#xBa;qD-{hxn8^pvVfI(OkYIyq6C}--tD_1ajc@t~ z)OtT&eckd;J8LCI!1uJTt_#64P8TaSkXr)UZ?Yx+`v_UYG7~tF9U)7($q6 zyz!UiMyq6w59h3Z^&xVcKRu!hmsU(}oYTS2X`?uP3dXQplz!&`%wn(m-`gdD+w4r6 zkXi8X;07BJIHAnr72KMwjwamBp^p@emh}oa6 z*b?yjhlhp0l0rcp@4@DFEMU9oeE!Yr!y#b$uN-gyIbhgCA*b&F8e=gFZdo8YDU84~ z{G#UV&-G-f*<{vLuFu7mu*XOEZ&cI2cQWoY+N)*zj&pW*jSEHOLI;#~erEEHIdzUs zq%e!(C}|LfpbH{>4rND$SLDAMCcO&sp5nJ!pzGVl{b5$@^L*Ffa-zm9`N{7iJY^}m zG*f}D`!=Kx#{U@+0cf&@;x8|{z3hDhU04doUjgO=`TkNZ?p&fpa+x@F+a!lUl-tAK zNS9oK^{*VNb$=AN^-G87dkcTh-KqXuZ#rS@jSi35({4nDrPjNK4qx~`NW*vk6zOn??0DAEI5q5 zVq*6LGrO>;6b~=5$bwKZX(tPnG8#t(>>A@U-+$THN|*ot_92W-m+kvhY78}qD@c*q zdADM9188NgLYr2tIG2-!5tAu;t%k3cs@)ouAJgreFCVWD#%76R=b|*zMselTEL>za zSpKe1IUypN6lw35+rqT@9N}2HNDu5RXePuyB#|2(asLvy zt=x@9D|Nf9kp#+evU4?NCR#8ty2nfon>1rJ-4^>?y7LJ-*NVfref8&Xm8imz62_Kr zwB~s1@&+2*L$yZgjEb3ng>TpYz9Fpm?TupI^h_kbPgNr&$nHt1pq zGW-BzOwNheA;?toyYtzWM!hP7h9sFX{piN@rZ(t-?SbgCV1KmgN*G*M@$^sodgzt_qO?KNV{P`SD`+94-kZw z0E1A}dx7mB**X%QV(+-^Ye&JymnHPMLGA(Z_s`Tk8yXx`vb411XiQ-=Bulx8Y^B*5 zic1!i+Im*ZJ+8IGSer)o;%oTaEE&Zi z?{At9WXsv;HlCNm!&6{MI_;0=DiunPoIZ7Y>f6<6K)rToF#I0sK~?fRudYWQ_Os^g zcl)_#)X{CZk$aK?W{q?L~AD*W5n{$==x97mMEVReP?x(@O zlfL-ZF%Li@+WLej1G6$f0|7QpGtmtCU!Txd*#BeFpDP(h-oH+JfOoWierRw76Nco9 z-E^)fv{sWfb|#O@sP^re{uc&UY(+z<-0U<6`a1Mo9l%OLi*_77Axj$lFu7q94I?Aq$rbLZ+F6 z)_Ksb7I00(VP2_@{|x(DI=|@RaF4DSZ*mrAdB?g9O@z8&VrNi&j( zkO_(8D$_V#U`98;zC5G$3j6ODd}(*}wX#}}Oe>@w$UXJ?E(e&DZ4n|W#3)0n!7_rD zx*oS3c_cvynJDCGj7yOV*N7$RR|p5y|O!dnPn$q0tI zRKM};VLsw=L`#2Ahaj+2tA-vp1er?pCxvxT78-t-QA8D-B@IcnsTAvagf-36wn@1kel~07d zOmeymNM33Y|{J`{8C`f1+}xS(n?e z#NI@9gUf}+pw}XM7KL{z%Sk;v+488H>3XuP*4#8&@XHevMUr=an;IfHq2>~0h-C{!;>J<;T+uNyef|ya>=xH#G=xfA%lf`q*7py z#($%aQ7KekB!1K-SS0o0WTuW3nidrb&rkO;;U~U~(tvUD{fc-LjJ(2-*ptOGw}r9I zx~Fc@N8hS~7RJ01?YN-1;&b7ZSEumM;yb+LK{$qAEBUPc8Q=HCsjgfvL_mZKEZ3eR zt3<_UD58ebZig4PI_+qBSoiei4y)X3R zm29Q!*$T_4U^K#4wBzNp%R8_yo}iTRNu-~cxk(8}si4eBGu=_aL|H<;rULrL0*BkQ z6f4Gti_tqweodg^b9OMQZ4`}uzg$XJE$^T9H$zl9(Ti0FO?O zLO)D28!U_?N-3v{Vlz@eupR}wLmBl3yO)i9Sh`j=Phf|{$EMg@CH$K0=PD(P!8Y9s6%r9ZFZMNF=dTB>g!2!Ulxj-G^k}<`P65BO?)U$joC{iwZK-| z3ko8}-%dXIpZ*|jYbv0?WlAR}H0QwfC?Kswg>P;fdN1DdcMq4miLp8E;B{QNJt-vN-;sJF?t~K z2;0u8bmXr%H@p2m&X>^j4wg=m@|_XIp*^L%BAAKcd>Lh7T@@p!2wOt0}2 z;a@-?80oOU0&8Q!XI9>jb)_$ag6VWx6y$ianG6E?2JyJyKU$Si1kO{VOLk+1j;37l zgZWZb*wo}ej=n_s&{|InLr=@K1wo4m{>e3?lX%5pw;Yy39t0UQGPso#H;FOF zX2y6nveNBvuPqj#*z}|@W6do4vSb*oi_J-K{6p%ZB* zhgm0wPPsyE@FG%G_%%4V!XW*4KJPHH4R$Vn5p?Blmb`8M*gQ>uUQ_>Uy2bPiQn)Nd z3OtLN_!vhr2+FJD3p9exckIC!L5h^TdR*$2wt}Ml&#QD-&7C_r8Q`pr?kN1zoKjeH z&kY0CXYDL?ReN$>R39-5hovmafLW~fMGtJMa24cSN}F)*{nQHJ3v7G}@(q(^04%507CNojN5!gjo3|LUL|q&L zU0{_gc650-{eww7sx~p?!{l-O6W>Ocpy=l!dWIx%uV@Tr8gg=KYOQ0un~Jb*#d7_D z(OGAwD8m`tI?oBnR0z@i-jL6pa=X<^%lW&%IUEda6&spLwtb6ZVS{1&!-?yCo;|h+ zFx6D33JS2GEHzSDXkqujFx@g8HMKIoC#jZ)uLgl4+QEf-6Qx9jBtx?k6C8B!gqVyn z^(Mr}n*;a-7rpvGcgr-R6M{ss3f{Kf_yfVVAd$M@B2vwnK~FjgMN3&L2G+uIok>(- zWS(f>6f^|S1@t@|4^u@$PZ=m*$F}X@*U9bxaFZrLw~tmDk`D&B>Ul-yl6~)U6mTK9 zHmHcyWgJaC>JJv_*A-rGv~~F2*Dne$PA#kpZ&9d+;=Pe(#0m{hsss zB0bh*=CPNkZVq@48zT)GE_sOn zKr}3h#g4_HbgjD5Y$vbXY@2v`w(@7Gxm>w=XkM`5W49bNQ z{@MscN4zaf`>M*Kx%;hrr2qn!P^|+X}#n+ zw?B%94kGZXX34;c!{`f#kt2r@?!EE|KA`{3O9z?z*uxgdO-}TJ%j-(lXg=L*cf!OLbl5~h zFjvP=9vC^|adSBB2m}53r!?AZEl*P$B<(Q(p1^_*B8hY8RFp{EDp!<*cYJU)t&t`r zfYeY#R+Dw_{@uKe;ordW{$1(R_ANw{Prhm-`!I7q8{x7CePWI~zGk8=BS8Ki zT9^?bloh;xKhTB;pWwuF!4nhwvDOd(AjplnA!ZpE#{LR;7ZbwTOCt~z#6fW!BEh|n zS~7N*|D*<)){={P%j#diL{5UJ=0l z-zV;d*%biuU?DRu*1TVHLiD*`cVoGW%Dft2AeE*&%H#K1ON!iF;EA=Ik4*3od_JW+ zTeYjqAm20i$>#Iq+&fy)(7@*TupQRteY?frHiYk{5J$jYfbzLRx877%2+qmKS7|y= z95w*;{(O^A;fo__z2@_1BCRAfHFc|l=Fl;lkrJnj@<+oYmkreazkYJ#v{OI;rZ8D>#D>+-|?|T#n}=(%7ve04TbFulPl)#h!Q_5D&y3LXTZ! zsU-jdSN8y76_=aW=kr-0d-OwGE{ji!OVI2m-Z^)pGJrGz?h~`o;3pQj`nyJ}#n|6n zZ@$?4*yX>GwmnAT$)yt~Z2+THDxWQ&(ro($(7=SZ=bM6(y8I01KkjuutrT}5)mNM# zZyNz?ss=v{a>_te9tf37bboj{b^%GISkaqG&e6zN-bfz7x@?>;UC9!$bsRl+6QyP) z>gQeL{Qf&QCix;cWK%MyLtzK6F+=*XvW%Z(syZr}dL19@qQvU_E7Ud@YwXi=*Mlq`Q3{TipRl=Kumlo*-Q8 zAJ+avr`w^CrB`bRDK04~0~gAB-l3e;r!4?+km%0=28C#j3k3F}D_apXllgH1(kTZJ z`}X0Q&pfWDR39q=BQ&*8sSPU;kN%$RfDN4nVBGq7d2O{gnUa;_AE7H@mr$S8t8}O> zg!IE76M(a`06yUK$Cb(~spzJc3J=H$@a(>8M=qFv9C#ppfsZ9@Ij&_#?TBGFpCXdP z$OxMb;_LtfHqva-k$KF!(*Ce-w2z`fBO@a-q;*z{45cDR1E5#slZ}hIG8!JC-N8ukW8JKZ zK>ivGloB4Xl4vuF-wOad1mcU4`4njfqA-h}Zck>z2z?Lv&&qwaZX+WizNugn;SAUl zag}d8~yedkkvJ;ttZ|7uu#$t|m)<{hlol2?tQ|_vqYx ztm}8%NuBCjS$P$Sb0?FluJ5BsWK>lCqOMmAfGB6iI=GX&KZ}|{zLJdFbc^NU;~lOh1Nd~`WPSd6LX{q3TAwZEA zu8kNZTc`Ge{vH9cmD6=5V;xd0dNNV9Qt(#2C;5oRFr=V}(pZ@8529UaOkvP$SX!B# zD`EbzNc}v4-9mxg8dsEle=fr&={oI&1N2u9uJhi=n8w-!gSj%1sjy&{I5ei1p+^@w zyzXsBX5h`YsV&x8n73_i0Lu!|2e~H9>w5Y%3!~%97eLu*U$8mPq)dXCnzx+TwH;B+ z>Xm-RmcIrdrhL#z0yKvE`WuaNvcLm!ERWX#)CP|+$hIe2IgS95_0kl=BI5%G{zA!fIO)D>FuYr!P+Jj}(D zxCTe$BRZF5rwhJHIgN&Gd+7ReAo!+8^Ftd;`NC3P4!(nWluHTw0weBOm$q{SI-iu3 zGzfTGB(d^TovglZ=glJzcQWeakS^UlmO|N+E z8N(2%KyNADZjuOtP^a7ADOanc@dI^NOk=Yk5e!4XAKgz{>;&!23HWA2QCga1kmGS) zG}CsAX>oW>x!YIZ^SsqTcRo%HK(V^tc-*85C>Fh(VYOvOGg>M8r-U=y^C>J^xZ7S#)@^?~7vq zQ=`xz_7Tgo#~y57R$mv60%Ox{(`SP&g%m1oy#2iMgg*?w{?32_?ik7l@>)^<0U9-W z8Xi-Xy8TRfFQ$nJr@6&3br{?Z;7ptaB#46?3^Le7b(Z4D~bP z5{rQAiX(C0zp2H@`+-)4awxANTf%Kh+LMC=0<;3T~hm(oDOX zodsW?)8#u%>VzfHQ)9B(>9fZiZHcvn6^||G+rbBImxF=@2@9(^ND6O`*9S5^cC|MK zOXwGf33`SHgQfLQZ9bs)%*3oN`!l2`B$tBy9Lh6#rE0!$VI63)@Ts0jywBC$H)YO5sqxRN}}Y->kIZ=m5oC!-O4o= z<~3#^8M(MjW>D4yH>m4>u>waJvs;`vMUejoE9ewkutbjA&)Cf5p%^xx>;Jmk`O?uX zrJ+dtwdFwTTYIb7Cn{fknbA4;vy?)`kzP$v5it2P1*NJ2$R#!S7p z6+aCPAg}_~M7x{905Fg>Ex>@FMH%?Oit>d&`U?s`*wt#VL>7i1b)d^y`);~LJ|7x zrIUt4$|b42YeZp>H>(@GVf0$Iirn@I)un`OSal!bb)W|<33JJP`dKECCIxXOX3V?H zLXbvH)aZm698MG0K3&r^cLo)XTL#koCdEP?t*o90>l;iyoMoB|Ok{EQ?`s(x<)TMa zJzHtC09aFaBfOAUS}H&mu&=bB?uM^KM}{t(xHhX zjxvCY$|H)!7w~DmOtHbM*#R0WoFEZ1im~4roTo^eEa)bQ{#Svp(9RR4qFLg;sLL^+pj_V>D~qMPtNJcC zHl;l;R13V-wP2S$>M9-6MnCDcLnyOa^DB~lK@ZA^;5%FqVGs(z{EaJ!$A$aXVG$9R z&R_;%xNEV}TKFopDF)BtKwpLCj+9nC-&)o9Ekj~5+=WHXN=%|QdS<1P_qXBtjz zh0|8+#rC8+jRwFJLHV`mb($iEyucTT3h;acNMX?9O;rV|VOK@Od zO_b{x5cVSL5~ltL?0FTFFai45RWZ){^W5yhg5MV>KW64u=(V_{)@q~|LMq9BhLF@D zL!YGjS_j153cvvKzE0UNOSy%^MEFh2!#12$JA2j`{d}ZyTgR4U6ku!#;K(Q(%4SRP zDW4sIpW+dEU{XiwRAn?U*v-)ALgl<-EvFH= zhu+tiIM|~v91fD7=&3g9L?8hIvlQDGzqy?**c?bC7md0-vreKLO=%n1IS~XMfpkkT zW_bIU3c5nT+koxFR*YS5b8`OU6Yt=aL$Dmk2(<|D;fUNBlTMgO@vOu6hfu3xqu$#w zL73oieTU{!`QPoh58_Y(n@DDmc76{;r%foqgwUWQ8)!Gy6Z-K3+(>-^$mrt}S{_#; zl;r93Gr7=q%1D0jQ(E{dF9JS@BZqZiN^Gc#?AR1?RMbgW#ZhwXw4sp{+*|8`KS68; z5hg4+uo}TT!84VGYPGuFUY}Xe1VZh*xz>whaqY;#O0VrQ0kvj&Kjc_m2A_<9(;WA}`)ne6Lp7;lB=MSF99Ul~ViC?jDOelh? zv;8h@!ijin5-7wETYLO)zc;v*b+Zbk*NQr&9TdR@E3R(X9EK~oBZ&z82%xHq1QQeQ zme9(9U(B6~IFX194%%L+up2msLRahv9Fv;a66fXzK2P^F2e+2#WlMs^*{NmO`?}vSWWLkiUn}rWS17QMr zNPeLNX9Ov#@&SV`9OAGg7TMpwQmXm4nhVuk7L>EYjzDCULIy*{Dz&CxWrG_KOUI`} z65lxl`Cart2Q%$R>)TU-x_$VC=MBWqEY^Y@j6-7Pnp4Z7-ELq#gwQ%-p3^Ks<;|@8 z;0>M8T|2t`A3h)?n6Er_UC$lwYxK0EkZ%wflEMB;s_gv1@_G{p+V98sR8 zQa1>{ua1W@lzEMiD42mi0dE^!04NVXe?rkD9|a8hq!fOSw^Y)B4YYBKpD2{M9INRd z0y9I;pHPo47D2=MEYa;xhYhR149a@G4oWcdu-Dkg!H6P z)o?s$E#}#t$J9~}MeG_(L*lUT$nernZfW9@QXdiY#lY=@}W`hOH$Q`s|nydlAEk? zAu&`b1rzJreKKDawqs?K)7)8at^>ElYB8G*=zajn%~gA+@JN9}+I`mVgY3zO2iPxC zcd9Ru0hQX#qk;ox&6^DMBGBQ76-+|3>rXZ#VkkU$RKo5GTjD(eKV{v|*w!EXo_}P| z%|$gB7^RsQK`t6V9g4vPr_Dw$H{ISlk>T$czyMhjY@(F=(0O;6WQJRgK1}ci z#?TXeXHQ0)9^PEj7LF5bgMx0aSWIR3lUzms%@+Y!SD%_w+2e;@4pr{#e`8p_!+qR? z5eP7Ya2>Q7A3=KH2;sresg=YCjDEtYK?`pw0`Y+(z_RQ`LZsNHan)fOrI8_M8;Zsj z1xRcpEtEYV_#cWyh$BIt-XV;k&d$MT8X)nA0w0F+czmMFb_)5Cwf58V@fcjg^`x_m zgU0tVT)XgZ@KfY6gYMWl#qu&JebQOXL`*{JrFinBy3)CYdhTk}swG=_1KDbM)ydg; zxszt|+iz(DpP&vO+~dwlv2xU_uD(BCK1;JX&KXn_sD_b8jVZU*)m{mM#7%d&#m%nH z@{8Kg#4X=tx@~;>{lvNH&8p4e2HCSghm}JN1e^8DLQYx__Mop=96ds5L_6A|953N1}B-!8D9sOV393ELBh`GrnEKGf9e^qUP2V6!xN&@-x9WFXJ zg^d-F?xr5Y6U#TW!1*r?=88uQbH@QaV5AVXvRD|FYnpqCRR*j@rsGb_jkJ_rHQ3+{ zGueyUfOU=MUSZgA7NEtX>vPe4Lu^=7?}J-$>b$FD(SgpJU2G(|^|nQ_OuMaguMH09 z?}Z5l1N9OW5!QWtxZL56Mz54EIEg_o*s&QM*AONkHG(`)R#Mt!Lc7bP`6Vv)ndYY^ zFOyIlj;2HhcUCdKSByHvnc!B@dONEV1_!s(O^J4YJZwd@Qdr?Gz^7nzIbBlBj9+ab zkPSapCvtNn8Q`BuVX^vtSLCGSZXZAi*7Z@~+u@hX4JJeAz7J|90F*yhEKm5T$4QGC zF2~CMEyZEezZIfusO{mWTvPK~Gp5h_ z4*nrx4P(M^jp~nJhC7n}C1!z25beS&X)oYiS-PWzMq;yZmKB@e{y50;Is_t6(#8hH zsgp#;gFI2{K~Y|bwv@ePn8ac3q0^E5F7$dzv!S61LN=MPs)0YBR@w-k6O~>sP`-~cMdBS5REeS%5qd*EgzLA zMESz;O*897WcLt$JnQt)g9#Ls&rqYM*&8H7YI+3p{$5 zmLyD+(J`n1_)W7%EzDkZvwp-?dSt{%EI|V_+V$9^_1N~N))V2={wbg^*KYtlUa43v zv!iSrOZ-PFyz8IZm_T|Q;+{*4LPTS;9Jws%PvJ#`obHC16&en@S?zzW%=MP(GtH6? zCU!TFxzZh4uyg2NQV=H4;EIg;?@IK#I{*0PrRGi@DYVq7exSvz4F~Z-dx*1b-NQ%~ zaQm$zLYNORJ_z{x%3)1SraqZnuj31ltw^LurVxP~8es297Gn;pc5H1{!!~~>7|;YH zYlJcG#1!VpGCm1yKkIyZyZl*Ud!BJcgY>j0!J$U+7I$jvw(5EJ&HMmc0XrFLJ0 z6Em3UyC(!Ox)443U}(%yUIGkclvsuB6AH5g0*86w>-n0=?_~q(vBT0qS2oWN zcb!e5S-l*$x=`vVp<}U=^&z~8^{r-Xn5-vC!Wkt@^%DCZFL#C#7@JHB-mboRAqoSU!vpICP~j-{rJ#)8=_qS_3;#mCMNixyQvK z?zFKab|L+8Kse2%@U8f;N+{Y0ya-T?JR0FbVq@`u^&3Y-MC1ZlnB#e4UMOs0p485` z#IFauZqhl2RG2=U{ZBPW^?Z^HV@^%!#Jva?OvZP+Q?KU0gKP0=YWE0rCxt;;cN`gY zH39hrjmxbpHj8*MtOVoBgLycL$v}2zK*#GYD8N7ZyI|)%Pz!x65o?cgI>r)}q6%xJ zKjz!-dq72ECb@$<_W!BH=q{>cBeY)8ADzm3&c**A1iNw}YI5}D@vfb8^>#Mjd<1U<^IwnExJjUO)&CrjsYjSAR)fXTte&jy7`j=TaoD_){$( zs>!@$xg-sS9-=Fi@XK>zQWvpCRxKYdTKr{ia{L~4RL86&-Pqt&2H}iIfTHfPJHQ7Z z^J00$M5^$dmIio=yTqZAJs=%GF^tDrr;k^!H{<8D#sAnc0kxN}ow3<|udO;?!aIMK z2#WgV`zjnUO9PYg=hjS`5lG%qk|Nzr{dvVoo#?LXT{tc8z|JqJX5or!Wh@h&J_v$_ z8B7)wk??7iM*jlPTyH-$KV3Z;nR>W#jIq{J-m|kG&QF!Ub~3;Wjc4f~`I^`ol_1sZ z8Ndz<%Ag4NtlEb`3!ewJbrdH zgoGC^M`>hKZ2MYMn?GAo&3ZCXZ0l0X5qfZl&pRLdo%zaB`>u&Yx}RdvlA^~6eca8Jy1 z0LnPUWf|lOasaE+T&T++-;g+qo_Sqn)cdHw&P&|;#{ttk#{HX@WmGXRbe-uoPyI^4N>?A973mj!H#qltDEL zn~iq3bWs-TepIMoU`=rP&B|>=W!@7AdGmBqt(-03Q@5^ZYJIQoA>sEtLYX~vIGt~z zr(LPlXubF7j|_te29%l*;jY!2#Z}PWp&x{6?!DBqlm3uR=V&k*LhuCQsvmg5lY(y@ z@K9m;Hb4-W59DB_s)+2Ly7h7R+-o{cfig-eQc_VI-mzrH;^1J2Qu%pUMb~DV)glBR zTfbLwTK_bG1kE&o>iHe-zP647uGUzuLuehZSQ9&eVkthyt$6&xa2`0w6Bbr(Owt-| zT3?p_^j=9=bLs9+gwaGTtbUlsaQZ;Nx&Wg`~R~#`K zVUW8*045C!Kps24Qrej=BAF7Lwo*z_whbNC7W<;sfx?-2gQiO4RsJU>EXs>fq9O!7 zHo9mbT@rn-8Ttj> z|EyVv-&EB!tlZGAJ0Vdc%hD6kOTWw2s~MtzTFYw3len$w0^MGo46F z6csK>&2ZA`-h!Z#xB+|)3=g<#2xc8fdCm`{Uz3571hQb}2_VC+TF2O~!w?uX8wto~ z;l$qr5v3NIz+K2w8c0J?*6R<$fXy)e*=EwjBtT;~CPN*-ykfVQ5%V#9!4U5(;g-OjhG+*x7_}p+s5QNbKeg<* z%o&CZA55Er_^|_3rD~p-!=RGhoF@4xCjr(-{SDw$qEKui;BtJ<>T|f>pEv?UQO(zM z#cnSI+g7N`Y`5UHS^7!O$0suMhkfAqOb6s=*DGM4XZICIyUig(cL0@8Uu~ty9mj;MkqP_HjA6no-PJ&7YqozL3c#U- ziGO#&fK}FoXHx+(`KP-L@6<$W0y!4{Ucaa?w;ms>+zaO)rAjFDQ^_FASeido>bx^q+D^7n zpmv#9^}vz|OaR%w7kt@lk4u=O#&`t9a=r|Gpuc~(H6R7*zf?0ql+p6gE0=!@10{$Y zkzv}y_OiTwe=B#bX(JMZ3p_GlNuP%u&g~ZJzsMK4WB@QOCyPA%mvI^!Xm$o=B;;_m zCYxZ_sln3Gv(0g90y$eIhx9Lu&8wE;UD)|enrSm%rk0!fY-{w@o{naVO{TbSNrMfa zr$iAIfe49g$BOD^d49T7nGzs>XlTG=&{eU*V zWO*!5Rc_1$SMf)u$vRbO&n+taSYF_64k-;CY`opx6n?pBUD5N=I9=V%!$35&VfYk{`>30-M!=Kv@H z;eQI^BmlZ)9??EZIiZl!NA?uTct8BRug+QkymZEe! za-pbiss+|Vu}}08dHlZuB*9Wrwx?2HvfHm!(@h+U_HNnEaS1`J^jU9x-vLs#{vnr; zdGF2M12=K)lk^TT+t*(w$8~qG_>Pc!AFt1%d_T=Kemu@?Sr*={2eeaa=4WPFUOWDP zD#CV#-YlXwsyMu}WOj((jk@_^abSurO*N=N?v8106u&@#B+K?ysF)jhV1?55e=wT^ z6Y)UYy@JpRl?ta0a^JN|Ka1ImJmf*H&&PUc9t7M=<)24&Ik7M$T@?lslr zkOmTSE!VCpFRDg!s=_=i6_er0?9#wjEPlAP|Gd$I%8k7cURJmk`vnB(E^w2o@?$=? zWI>KpWj8wnBh)ybN^FpYnotG1S*!+Fx9~pMU;iOvs&K|=LtEVA1pv8u z5iAF43#V_79r~b&C2O`QG*iOxNEA~H31TDwQtR2eq3~8(PiZZ*P+wsEY%f=Zv^dRd z3{={8B(kiPA>8(>>ANg+uDX=6iiq3icC}$4bJ>sRb##nxE(cng-=nlx+&|Fy%jnP? zUSwcxH41%aVID`yNf>0<1q#PHzojoSFC4pU80=!?=aUeHT7y+kO;Ser%hrB@cJ+*`^9v{9~gd*=mtSY-(19?*iHyG6Ic3W^rf9cqtdc6PFDH&ygo zd=_ybOP%4k4+TZq) zERf{bc)0HEr@i8;X217-_Twv@T}IefsYAnP%N#QMN8yUO_7;V+c4+i6+_KS;nuPWq+0``NCoH?S*^hh7gsL%%qKB8Mmk%h9;164X#&K z4c230*G3a@v`SZ+*V%q2I66{7{5qmM@&j-JV&7PzP)OtyX?YB-v=jxPznKMU=2+xz}bbJdB+VlF0)m?#S4PxI}-OY{HeDX>!;x&QbnvA_C$7R3>N=6sxz zT!9{0ma-Jl`i^}Eqf2zXX+hMAa{=CYcu>+P-ou;Rk>fRh{kRFet-B_+U@@ zs5VLRDR{g}oWk%^_(A%p;*HjTDLm;T9($cNP1S$|0P7du&IA9FXtgejdWR`1N*=qH zE9O=0jJqdj>bq|P$kGqKXPh%at{H+tw4QJY6qSB>^36%Y|&CktN6(D4gq*n3u4F9&m7@(V-PsZ4D2fk420XjX3z9 zwL!#U`a`HXuV*V6Z{_#OV5>C*7Ot+>Bj!BUIhf*ji7#Z_Q+Hj&2GN?H{I%>%y(P)I znE0B4{3r3Lf}k%CgzmN&S|yH9SdqxvVgs|l?zE#B5<^XHv!f3m)Vfi zYrDUa_`MeKjcSC_HiqY@kLH0v_cnbg4e9JQ2D{#m7+A1|pIUz+aJ0$R;RFy`s*MJ3 zxa=mTrV$jZ80NW1O08Jqqp9MSjr7Col<=tw6kxs&E27yh*7j?%;*PJP*d~FJP`^?7 ztV?&}xaE>9d|(Il>g|C01F%Znr>US`DUWfSibO^AT9tg;olYkNm5n6{lq?CPOY;-R z?0E#n+Vt6~s}`u!^S~Exr7F+RLghS}42T^i_r&$$dj*rw+mBCa>osD0DxVq#cx911WQcPBddP};E$*)yS#;aI>1Wbj zdwr$h+0h$J;(tY>%CI8{1Z!;3AK|av$zg=I;~AZVtbVZTh2gtHZny%Qe2a*=cTqpR z@-^8cWSCqhofEgwwJw(p!~wm;2}j#KXIDM2~fJ71(3 zCV$Y8w5BruP4x8x_!75)$r5_A`I5BNH)$4ZYm<@_8qmUO_C2u&>G7u%&P|v*boq?T zhl4lU+)QUY1t!MScm+m_x92<&FKt#HzP~h69qi4xX<_qo9c2R|YhI@=s=S9~7q#%j zvVkEE>v5bp--;-GOTvCS#OO55airX^Ul8Z^mw#XMoCmu=D04Nt@oq~|a@q4Kay${r zoE{auOi7~!amkwHM_saLElDB|dE)JODCSro%(6h1ZGxK5XdZJdEMeX%h`-)2v+$Y! z>!bt#*ji|SqYJ;T)^}=*s`Ah3qg-~Q@<@L_Q=6p>%9h$RGqnFxnx*hDCuJ*-z${i0Ud#+fr@aEiN9OvD zc2|Rz7}FkEh<*WC=k8tto+uqFkQ|`nRH4VlC{E}P6ah~}&Q=L!ym^!*6zueHD8M&+ z6M4xHc7?}gXFneP*&Y+J&48zo?7$`DiJgQ&7eWazJvyA z2P`3BWo!hUs`%AY@#C(XSQmz%^X}S;O@TCNW?qp{ZRR|+oTP#ug%zdnyuylF-xo6U z1)<+z?*`qvRT)$O*rg&oBp(bl@a}j4FZtG@$7lbtW>rp3p_ea&N$3&3w-FzzHtUqL z)GHz1Sn%_b;a7}h@YfxVoImV}OlOM5`GH&`<>oPE&F?GH4jzQ=t&P^52C@1sK9V>g zBw+|KA3`BO{DNfw_n|C1;xn79RBP+FGZ}z`wDl^@IoU~*TBB8g@=O>Z{BnnD^Q zR{Z$auKaMO2`e#QT=koHiV!g=v3AuXKxQrWVYr zYO_B3x#Wt={FK>w2M>H4F=8}$VHB%2bY-_G@210adhhGxwmrn~JNH|GrI+MAO)VNn zZp4mUPhBiUcP)lP224KUHg`-vUApVDO`4J-gfV2P^mtSJc;J(>+}OE4PnBGW`csez zeflI!$gs!ias{9SNLoF4F*adoz+l%In#co(XIzIUrw4%pDAfcM1l}>efrUN)9*3;Y zU<9ULVUA=U@`j{Q(4D6J$rZDuB>wN2#vj6UU&hG%0m8a^x)WhhFnD1M`!qedk6^z; zX%m95j6(GDDp&7u>Kw(~1@3+P4wqsI8`OK3zNX8~6bcR(jz1j}!9@QT*>5H4770Y= zw||+}m=u?Dw@N-e%6C1WW+C0&yuvB7Qz2)d^ibkv%*QtcZqG%QC#0W09ebLXbMb`$ zyRFLSF-kS_JM0n0!=+v7%U}0jBQxoTHj+8oJPQg$Hw3)nDfvPA!9mj76ulrWn(XJG z%P6Yp{DV=3cpBczi$#MAAVHU)OHUO*)YUf6>)$61ajHuT={wYon5G@Tv~fSHzzhOr`Fdacl+s2bo(N+O_UOCIl!oH#pdqVE;@1K9xES;! zJtzjAF+GE$(kL`F5$*4Q4FdCFn@QpxGw9;3Wy_be4p%^U)44d=t*{HZe)u`DgoYjR zyAT>fWk@NpUzgH~4ww$b)(f2=Cjh;1;)FXrGpf|ncLwlm;&uEOvqkwep*WjO zX_m*1^1TL(dHM?+vQf+N#g441oL%e!Uy>49v83ijOmnrZ-%7TyrE#rp?Ia0s^&NQ z(s>BPof+U;mWS;IdXA=1WLH-N&D2UY7R6^fd+L0CY$=fKMMdBqbyJe|b%?r?=}@&v zA21^yNm;;0OM1#K+}FgUN%Rpj62zWcgL`yFX@1#MtPVB>zG8UkD7$pTTh*w6Soshf z9F=AuZec@i57ENBy?IkF4$7PoOQB-jdtYO_2{n&ysMnU(GaM)walg`?EblHA6N^jV z$?CQ*th&+jJ617U12Xx>p-FJjoT%7PgnwDz0F0S~F0HX8Q`v z@mJ(SX$GMwq@RvJ)S*&z1~suHslCA78v+~&x2kCJKe+%pF4K7^TE)cMq!X)(hMZoW zFB2&JsZb-2zO+kNIQacFp#hYO5`tpNfj(6x%923L*d?Sd6KOQ78p?*C{*hNoM?o)x zr3(r`m;!XzhR#qi)*b8T?}6y2QLg?{^z-VwTs~sD;gX@XQqHv{_9j>fIH+vgBtl!6Rpo!{HyLMZ2At`;7&7_QjxxzO4c1ydSifL}% zK;}gQJg{L8ua;zJ*%Ml3c0N(?Mqm7^BlD6#JnP__MW&8l5S%<tg;)R)iM)UN18pV_XK0gZ6X#@t03_VkN#y03xUN~uPvPECa)$W)_GM&2Et ztwrP7;{!bf<)45y*fs}nFd~lqkh#fhJ)b1Gw|*`CT)PX#=?Y9=U|qeVaPc+@<%5E& zxVTSu%Yf*;KtvLkw7oxj%Lv|LuQS zhd{z<2swy|tR!IqSg?Q4nb1aUAm*sCBe%T&A0-t=24z5ocaZ63MgCpqul>3O1*Vu= z(BS6(X|;GNi|ar diff --git a/deployments/cli/community/images/migrate-error.png b/deployments/cli/community/images/migrate-error.png deleted file mode 100644 index f42ec441a7d1c7cb52d8dd24b4b1bd4311b843fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14139 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 V5bMF<+1r<7l@-+GD`ZVW{~rQdd)NQ~ diff --git a/deployments/cli/community/images/started.png b/deployments/cli/community/images/started.png deleted file mode 100644 index d6a0a0baaee767c3e8d852926e1a2ec788b90f60..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21489 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 diff --git a/deployments/cli/community/images/upgrade.png b/deployments/cli/community/images/upgrade.png deleted file mode 100644 index b78fbbb6093b60a8855f863fcddc329b8472292b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55240 zcmce;1y~hp+x`uLbfc7Xh|*vniRBW>LQ3p`Hby&1B`O6KhaKJi(A(mOa#_|_EX}Pt zA|9khZso5gX6NiHc{lj%a?z!aA0JN?h-GdUj?!~VuQT%%gMaW!b^9iHhCudxsg#(~TY#X+$J9iJzK z2S=mQswWjgD&Z6oTIWiEi&aoW%)!z zfZwvZHv0M&wnmnA^skl^fkTZO%PH9@Nr;1VEzKFU^(=Mt863^6?stLWa0CG_&GqfH zNgd72ENnrJoa8?~0Rmp%zs*Qa`r{*ZrkvzT64Io6mNxpNuNjyan8>-1NJ&XKZ1fC3 z@Aw7(+#UFflibM8&I-iH=-}YM;P8UM(#DXHnT?H&k%@(og@qpY1ih`3g`Kt|y@f5s z9|!sCIQ;syx;DmEcE*+#r1!_w*0BWJagvkYpXfjT{`j8yj>dnV$-?%}%K|Qt@qP;< zGXoRje~u08%5ncLNZQy@-%Od`*c@mc;2c~mY%e%|eEzpBf1mNEJ(d36lbMO>mtB9_ z^v_-8ZS`&VEX{#)+Hw7TYyLd%&l~^Tk%RI6(tm1-Kic`nyFf#8A#pJN=caKX;Q7)=9Lb0$qM5%H<7w+7G9LMiJ&3E+8-a{$7YlhR zU&@_&g)KD0nG}~ih11EaMBN$ixcbu)Cg`V4u<=mXS`WlM5&!<#+->f1K=(GfOui6qjf;5hFMR{Pacs z{#H-6uhIVgf`7k{{Y(os*taA(K3;gWJ0^s{9QZ$-!Il>J+fa%UxKZ9&u1JG(D;N1A znhb5z!{0B}NxUhh<7Cwo%;|jmc|F-BOf-&xe)`0b;}~*x3n{Z&zCYaCBj`Gf{F3?A2ALK%!7X z;Zcm1dz~wvXEfj7_RezVg+%nw(9p)$ey;TQPgq5XfOAX1z@y0tY!p<^r3t<#5Xn@y zOag&L*HSo~SVnVXsijZ*J#Jr^PZT$oGJZAef0~5m6@2ddTgzI(V|5y<`$IYIlyn%K zY>kpRd}Tahr&ptfLnYuB5fOoHWny5EB#7xen=&nmoZgLvyYjdh_Ym>1J6ikhMWf+% zA`h}zL0EjF*NNf0m6K)k{o|`IC{inanA6p^h6%AzQM@T0x7Bo(F|VDEjl4_P-LD%>k1!7%YU5Cjr?!wo@DYyKu zOh!mYLYm))5wQ=g_QdJ4x%zG+?@e!da)6#AKt30GB)0$7y#r%BU!xHF>z130wu~js z8>c2!-Q8Ygc%d<}a4f9%r(^+r;x2Yl>$v3&r`3XI9hci>SzUX5ro^Xio9<{dbjG^g zdgl|Qmj~NLjosIswg*cBTXXfb=2Jn~)GJU>EV-w0WFx64`e5`9gYO?@5?J&%TG6>t zP#*PuVwG}k@;UNL-nLV8T5Jnw*piy=xw{}?Guq#8MIUawJz-49P_48$ zjE{&Qg=g35ecWjy3NR;0;@3SOBlGsfPXDo4(G*K$IIMI`5xx4i2PUvSxWmhjss zucoq!HY7@3M0!kv!7&uE)D|*aWxbrO-RggQ)X%+cI^}WQ;i0q@5Mj|c&sbl*AMA0^ zhHJMs!`5?6lPVfRbNKR2@AXu<=_8AVHz>x}&8mbnHDv>JOls4WmQt<$sObqW4B?^{ ztNnLos$W&-WTVSCPCQ>4Mj1TuAq0jSdANE4||!o z{l$KFipA+{Z#H)F$91&G$InBu_J~l%xf?ouyK9}u4_KYX?vA+cqS1&z{3NdXUdKFA z@l0?19Vp5;8i#it@p}vn59?=N<|}_)QM0UN*~jBsc&`{06B9#mTxU97cwE_d^L}Af za4L#QOip1a;~|S*qL%a`$=!JX-IGMQ5*>lfQN^=`&2~*nxU{4_#X_}6Vt2=jg8)-1 zrWwq&@M>UKY-_^UZZbbhg~K3r-XH_nu7%R*IQgPsydvRN6U=Lv*u*F-^gc!N-=>(? z{dKwpX0w&pCW&OQT%gkyI>$a#bFp7)HP8e+@%HXnn~$y+14xWkIwNho*yuvx&>47p zFqh1!y0gEf*#Xn#o1{L%c6K&PgUUp5Db&M>!W`7(woB`Mw^D3 znuG7G^B64yrepaz7RtoCc6XQCMaF%JY;4_m&r$Jwo9fODQ`}xJhzPZVx5v5=pz`xI z^AzY+i$LAad^AwK7c{bg0dxplhe~)2gD~Xt7BaP>cNhu$YlW%rQg-o`5Dic`OnV#`^v(0yxlGX#l~N@OVbfp@LBF z>fYp6!zLbX$NkT}Q)vRoPW+#mk-sXBV!Lt^%*wO*&gx>;354Kuy~QsNX6GGl#t`Jd z*=QwwpQHtXUb_zX6m6-fkRg=L_tp(QhQU?u_vU`+^@(G9zmP$rPO0zCeJP0As3VNX z=Bfago4eb_4qr%Qk*>v~6M;Ug^(BdQgp*`7xLtL(4KaDBQ}4&?Vl1`cYJQo&yFIfZ zW>e7BthUK0j5jAZ#qCh@K;W2&?|Co3;`4BEUA@K*5pSF6npx^PMo}Y``+5Y zG;wvZEyUDbV#86P?2|>bjRPAJ3a|CH=nOnqKQ|GL?N{eE}kJ(<~c?l0#yqtCXx1unJwLYhUi`?0qwvRsIr zQGWR7vYC!a!lbKrY3BT0ux1 zZ-tXXvW)NY*EhQp^m6Bx%LzBP(rjvRyFOV95^Pn);evB(E;@hfFIo>ChIkf~Sd7ks z+~(6u!4^Bq<}r*zS96-f&yQBfKqbac+0-m()Ni1b%bGpeH@hwk2`FQ&ZH8K!=4L0~k5aXV+V!J9jN$0&EP=;g)oW-x}2djN+V-sG52 zXhY1EH->PvRs9Q{+j}$yJkjyKtX1JKTIJ*=-|?H?u!lOiZ{ezY<^=HapD8xVhPfgb zGODg?gR0Fn7ThhiQRKc+|4TQLUO@O}mOoD;(fw+NA@BCYD2k&-RGu`BNg<{xIOb_U zt)sn4myKM7g^;K!w+~BH<>2gYK(^V+&=++Psf>|TkF>l^Aq$oY@M{#qr0EKR%YxJv7tATY~0VTvR`66M@;)-OY(LUyy3%!~T4reghM9 z7~aV@+z-*@!~I5R>08!Ht1j;NJ!WHwrTTTE?6E(&kA}{Ha zW4tQNL&pAKonD?z8USA|C=3bRW6>%h#*A zx&M0FpF3hiKX`0_IC`q=W1hvCN(?n5N>abR_7fkL(l-$x)<|(W^fY^-y@GKp62u^} zis*o%SjZpa&q7;w7(v_krBl~}%Tw=Ye!3UG)fd7WFNOX**|F$lZir$P>-eZMLvkb- zk365-yj63xS{5bHkzpo&T0_LQDP{I6`BDXW&Wok)Hx&OeZOq_2kZ~G2N)K2^&!=__ z$g^`5dF`+}gDILlA5+{pHEVvkE`-}s;T3U_ZyJ{)nzblbr$b+p_Z?aLNO?AtQPGdk znrg+>C6SK=MN-SpVLsz)snTdz>&n8n3!hIb;0u3szw}vp-o5pJg4eO2R)ue@|1!aT z=D`e{RO+ZZ6f?fE3}VT0pu?hVVvWp8zbkdYf84muWpX^-rn7onTUcobHzhFPDp?|x zM{)Y9PoJ46tpuLa-Ug!1@b|`K4${=d~3s~}a zQEJ^YGHd>H1cT1>OEY@7@Wb1L$##9=Si-o~Cw8eOvv=H1kWTKojF9I}*yQ0zB~72s z4lf9IXZu1*%clo8Iok&s8o8pWXPYX_CoI)P5(v=Wy{Def^znGYlf0em5QDSHT-GPt zW6kw%UAP{$to?F#SEbpMh<33yI1W?$5??M;lD$NIMxg|B4PTj`{?OcWDfW5y;}^eE zJP+FaGS!sY_{`dYlSZ>KM6KjzXHH&6;5_B{;hUI$>8bHND6kVs;m`!Q+P{TMK>JwH zBJ!7mRW-n0oKLm@5*w_`Wmt12cx@(3B^LW7iT!J3dPsGes$4N&0Gixa0}17;kFQKO zMsj2;vopLkcP-tIo~YD2ztY#!n~CaU*;e(Ap{+-Sy{6xz>L{eBOx3rD`uRJNG9s?z zm2f#7>gCF2Nq)ZbxIIcS29UyQTS@W~2rwSg$bClVzGm85x;wSJ6O5r%WGHUGtUsF* z147pf0A2?G;M#N4y}5!kW|6p`lWnaNia;9>CBFm6ZX+X3BMP!mqT68vT*u-IoRV6D z_ed-5Ujs(EdkUjCbBjv$%+d=Pqs11#(ggjU z*i|802fHV|pzCE10D+&qcoAKwQ7Z zMs+Oc=SKPrh1FSJVsUCBu7{_WKanS2Boh79SK0z^D?;Ma>xYM}wjB05N;K*wW2<$i#GR;)*|W$Z4G zjh(j(DzmMZ+gEI=zoZ^?QKt-a$I$8%`!+Tnv_Cn#?jZ4?M4^0EVuHT8&(d(Q_URs! zy1Tp6zt0{WW1Xz-EW+?W~#rAJWLio7NQQpp-)@x2=57mX`J_vO;XSYU#s)j`^|}> zGXStT9(T9U-`n3m5z_DeKt>4mK0}$0MCUqR=wlgs{2^4_=GyuFwa2_z;X!zGqdhs> zyP+;R)kH#{sfe%cRB5ythYUAip#|KLlZP5MT-MxdG;vQox|PY}&dupGqw0oPqn0O} zSRgbKeQ{IKN6D~vF6Z4yzVeHY><$dO^NlG$6Hq`rPDT}nNTnnsGLF{!du&Hw z_=s$PoE2~U4UnslL`WfiX@nu4OL<#@<1M{~i2by)*aj^1G!Q-SG=j@|iGMT56n z7|1OguS83AGdasw@CRYR`e6DX?+XiJmA+>IMphb|?OLZ7$_ReY#&OSkGzIhR|J+z+ z4BN$)hsIX94sTa0ozZ{{Mhu+M_E>?uLat^b7Znv%mua4jS1%5A&Djfuo1Jbl&Y4=h zQCz$cpmRq1Xh<(7Im|`EXr{QG;uok^h6Up?GHlDzR2U60`ovoTp%J zk`AcS*cKGD#&x206PfEjjd0O2Wf8;0<|aK@d8Qg#H~c#4tA)TJgk9iv#m>eqFHU{$ zOg)HSsot;!)IH@9Jd@5IWOJbR<(ju|YZYPfOmqn}FGRV=M>maa+}CX0>2dwQiMoh7 zAlt|9m(gg15jgpE@6la8h~@)ne@jxu(VN8}|E6=K$9RXpNK04#YB0F={nO^OWn;!e zWE|-@26dx@r8br;AVKv-kr=u8dot%Ij7#^80WxFZCQ}56X|@cr`P9ozb>D!~m9FSg z;@&uJ+ovvl&rzN@s%d}o6|FiQ5WZE=_&GSm{Za%CcA>q( zeEPu|n0f#|Bx@0!3<9J$BQCePCpK{l&^Bn5{E#wfv`Cu^Ig`*9hx(E^O(*AoX=~LM zpWD68wa>AkH2^(MozK^AkL^oVmTZ4AC*q~9#Dn3NRARL@sm^)Z!gKvbCoodNRW^ZK zGFX=klP3>lU;}e`9wBBrkg~i*+;Rr8iW?z}4WFr#9eL;4&yWRc_p0*_9k!S)mSpw5 z)gl~?6-i*W7ei?)&{K1+&pj6Ee57I9eH^9f*py>VPaYnAZH(LX#!)nu`7BceQnl=5 zvLZznbMuMKu=ZtN2H9NUQ<>J3>9@2Oz5(+WCUNX1)+KX41>Miy-G_?bEl!?G`kGwY6Hyq(l&;Jsinl*?>HowM zfsKV_;T`aBj&$FhZ=u-_lW<2uo_Nc4y|0U7wR_Sf90Ls{{8cD#Z?#|3MhJgPdDf+D zhUnuK5?CK7rYRqNpz}7zVJ4AqxxR@&Z)YY)^nPE#qcaec4#>*o18m3 zz6%T#42In%FPPZfaI74M<(5e{jZYv3{R);%90yPWOOA89oSm76O{;vgJhc3tI6 z@mQZ++g;`uw8S90@1;WCQM!4ha+ysqRU7H-??L0i@_s;UStjwgw)Q?iD{u$nE@)}o7(j^A*PRD%C zk0JfO*opr=ykK#`@0+U{o@;!1Sn6GpO5O=eQ1kUFpCP4BbEd&K`Mx!83SszmcR|b|sSk^Qztp>a52%h*X`{WYN9+i<^eO z#9+^`U~N>Y?P+#7F*_Hrwc`d6IM#t|e;P2G^yn!0768lbP<*>9*f+X$o7HATIDyz0 zfepnlA1=J0lJ}u%O==$*pTF-@0XJ0Lj`gFii{tLEbLFOrCcr8`0`|l4)PC5Csy&S- zJyB(?8%DPuVGo&8-@;17C~&VHeLJ2k39qhF%(X=b2TG%bLnC|!hQp<@2aygzW1=gi zL-m{X#L|~$tFy^_JTpZ(ov2S4+{&5?u1HkZ#JKTnhNCMEfM(gL!=O8tEH@q&EPWU* z3_HjaDBY}uNiJ)pQDIiW$`*AfKouTomssk1HiePb@&rK^H>8^_*@6bw-6zc8rdv;e zAzdDK&hqGEIAQq9h>PBBAqr*3)uIqfDS|ds%M|sylZ%6z)6k;?7TEd0dSV%ok}%=3 zof>(!Uvm`C>R{6j^Pf;N2wR3*v-pIHOUbF|ww>h2X2B_F3u)6q!6&fUX3q5B9}SW* zDfp~^ZdF2xT`;9f_InFOcj-U6#4Lr|PTbPe5sFU{q8)A%NE2X>d7B3tVnrsWQ4m{j zm}3V<)kOg3a?V!pkld40A&%(z>owMQhL3}bfNZO$!;&AaMwK!2t6H0EZbH-Avllow z1F`CCC%qAhLGqeRpS_Hteq} z^BDaBpH|as+IQAhB>IOaS^D$>ikAh))*jisDiBS61^=w-i@r-*sO5o$4LWvzwh&A= z$eDs%K37Dv-WnyLd2z- zykkAn8d|!BS#_+cmrQUojF~a~GTjWhtAhE8V55><}+*J z5VY#&xO+FD@{LM8&*2s?(}wcHl!VcIB=F8lbWG%9Apz8$F*9w+_IiOB$40Qo!}#kA zLbCj8qvZj+E1@BZX@$|qzs^|VKTuiJ&&nUP)hEVBbmGTw>+j-3){N}1D&UsQN1u!< z;Z{A!h(omVBfuHn%zr_nI|yywrXqRf-kz(OziQ`(*nv&Et^1@F5Wo^pPpmw3>H;oy zN`&eBK~sa}9sbUAQrfND&G!a+-3<)Qrl>xOxlI>Zr_@NlJM~I74XE(Gy zB}PlT9QDcT?4J0ro68dlv*(!#M&X%Q`JdklbldOE;7gRa_Jjz0yVvqi30_9PAUTvS z!srrB-)TR-RwcXmPQZ??g$h+}>8i2$;S+>U;{^JI0}wgvPIr_q_i7=Br7Ar&fNc8I z;X{+dLmyDOP#FO7Kt-6ql>#9}d3_B?`Q zt{A{qtO~dzM~;^sx2^zMmy35?H2c8k7!POKkbvo;PX0l536@<^REKX&MxJdJxcWwIqcDa zy#mm01`%1R4#>rUv{--N&{daA^~(#{KCJ3*mgy6j-`U+L*us*ij|zuYTyr?tAR?r9ezYr}&R*ePNk%kU z%iEk-@ZKIwHoBBvtgA*PSjU7iHdLCSU-YU3tq`0t;Ul(nR&qYBRQkn$5DWz^ln>Hs z)Lj3P)phfMX$(DaNI|yN-aMfJi1&&w_QPMJ$X}UUq*9)G#5uJkI-88;<9|s*kWs+lE^LNS>OIjfhFmAI z*{<-wZH4mN0i+I}R{`GLv>V7!F88~_3v5n48qShJuS2G39JQUO0n^4jm-Sbvv?wU; z-tCTxidrd`ujVlb`2*37kX$--i{a-(fV4p(78aHsy4_8gGn)N8Jo4xl=Gy^2^i|>Z zCZ!UcwtLsZA-n#`EI-*sk;^)y@y@-a!g}8Qs^H~_j9``~C74_)yGgn+W9XV#nG*#Z zxLt0c_sAjAVJL3p#tgk0zT)XuJLUrzEUB>4woZ0)3bt(p%DV>H6)8z-p-Sh;RrmQA zVc$`gr}^pmH(8yWHPO#-r3AYfSMoK&Fx-nM3y<9Rm$<%_az?HZ&iG$1K@C!?BZ%5JHQjy~xm8wZ`(5eRuQsrEa_*B4-2sg)+wj zodh=^H#9^WUcGwn$?&}-2H6Qa4>3V-=f(#-#ALg?g?(Xb+Rl zMzPNEV1Pm92k<=w`bJFh+p_Hw=gmpOlnneA2C!RA#D{nYo@RiEZl&q)APG~%liQKR z<#>baWHhE^m{EmjK+*9l-$Ufj+J6BbQ_5s+J#f07^#>H_{rLS3fLK^+%0vn7C*$Ny zaz5R8oQg{hE}smICl`C%TajfPI%g~xV!%e0O6X`J4D5B0}%e|JiE;f&4zr&(4EpoPadSlVv>?HfpkhqR; zPFpry_%mf6Oz4JpJ*QBwwU6;pUXUlVd6k!u{LcH3S_f< za<3E!>q*QXa}zQIKRL%>BM5=qqz>x^zZDH6bB8%#M>s&{`^13gGfR)+QlvT88nD?z z-nvcEx=Z;rbia;o$O1)0l%i41-c5O_@@1ii*dhI#0Gjss6~7WE|Fp*YLl$&k*jg{8 zaU8<`y!P=dQe(uFNHPQ*v$P>vM%;ZkU$<&z#l-ZCLm`&*rEcxPq1*T6T{ux#j}Hhl zFZgheim0~kJ`Yt}n`z$w#*)p1w!}$?Ij8kZ*Jge1{?_uWmTgREPb*j2Abl~=u_9t! zSgLK=Aju!NNcTG|Rs%6Z6wAI<{~nu`x;LQu52c55(Y%-Ftq=Q>*h|U9tDNWX?#x{< zFU4d#*`<^(&-(p(STkahyHUp;5PkDCqJzfpLYumeXKTO|zC+yS3qCe|XG0lcF!Mr5 zdmFW*>?wdLip1?Wl5nGW1jp6E?lP5uDiyqCC#`SM#r!G>h1qbHHq_!Q5N!n$t{j@N z2IFqsrSnY;XS|;&-*rOL_7YXgs)UYOL&L7tFI>6e?=Xuu72E3{^Ssdbam{P(g5wp_ z=~|18D{Rq-W!|R=K`q1mzc~7&DG^r6YR+R&AvB+Ah zAN!uSVE6=J`;P&6R$I9s`so@62m=^VGEkv$X6v)*eQZFlL;^;GAsT!P7}-96d_u6; zi55}?zrl<>YdV@L)eoUFQIs4d=_Y_H0}{Li?z!}?xcf7hhzmB=D`_E-$FgUHcJuI~ z7W-27E~O6W9Jd!Z~TW zLU^x|jJoQ3!DGU5@$$SZBi#f|^;dYARv(V{_hb|Ui?o73>umqDQ~CdqcH zBFowQ)Fkqv48KEf<|tCpPh;b|hB!pK#1GW}g`+X6zi6U7iee8Cad;pWNsS{z&H5yi6)o@CS8&OXIWX-UM#m$#Bfq zAz(t=aBTmWIm|f*DmV@)Zp>79>Kx3rN3Aqk9+x ze!C?`xvz6W+@``1#WmaI^oM*{r+T$4Y0y>klMK3GkJYKD>vp)(Wy1~5V8T(DF4p#` z3EiA1(&TPYP%!a3HKJ*b)lyo?{Y2`$KUDzDwkp<+c0K#E) zzw^e8aG~}>?d+a#>0B(w55{Crt=Oq3^PqmE5}}0qrGftt?b{`1aJgC)mv-K3v&O*s z*%C$9!qv+#)89c8JgNr$^I{8trtd#M^ArG0a=z*k0erpTObM^+egF4F?6JjEF&)dP z_h#n^L%pWIDf8qpt-=5h&5Q3&S7ya&x+g3xE$MNJ#nMG~$fOGnZVqRyCiek`%qXvHi^qH3_J5uh8E7?#JlR}YfKp5CX;@~2vE0I{RHesy_y z`8iOrAf%ADK9Hx#=)9I-XapFFy{;h*R}AWPj*(9#KI&avKk(y~w1XJtUjki;JB2-y z_;_bhR`M~M%W3{KL(DT7Z!J8?RCx%{*EYL1l>#^b&40lg!g5;BkFBddnj9&DRaxY*R>oL;aeR6$LSSV()r_z6r%1;({uV@0PT8? z??uAz0Ul)~9dB+2{k?#JCXIE~*i)Aw8j}XNkOya~ZH@14Z$`4DNSyBSiW;KeoJ>q| zXVMA43+Vo!6(Dx8xjP)!^hmI)^aj~?@1p7|XmC5ttkYb$bw?S;Y--jc#``(R$h?9d(+b`@aP^Oo=1j%g98V_= zsdAJWg=%a-V>sbLbN;FBVi^~lrOtR;7~`h1ozM2jAzv7~t|p0X(9I+aHg%TZDU%8H zLn0lf1Z~C&gm2xlI#UuQ!z@lpdu|$NBN6h>#Fd1UucRVf?5JfJl0!deb}H8sl3i&a)AH}?pqM4o zaYv%jziER?!qU|C{bYa77|RMhg8I-@7%@6u`p7B6WJkjTNa{6$U)T; z?=t+wVdjA6)gg6k_wDz@uj&NrLl~Bd(bF=u~ImHZ)wo2;`e6odx zldGxx5?fRu2_-VUGIueqEv*pOaxo3^g_>UyG#1!i*`eUCs&2v9sUI8EJ35s!=$u%o zqf5Hx(lb!>9JhEMBGQv4HaIo_JKtVy5-%-#jY0w}`vkW9xpd#;_3PV)w9u*l$*8dv}=Af{z$a{m) zNfr8`k#PXux9Zw|p+R3-#fapqe(pPQt`sS796;btvvR7`B5%xmUEpD2{MZId+8yv@ zuu{|J`UkFn5ah-v?|jipW~M2ib5O|@f}ZqL7Udl;%E6Ps`L}_ZcvE&Fcsmx_6D~K0 z#9ZQ9ckIMnlqy6)|*>{3eW!};t-yO?tZj&CrNL#qp$A4ole z)vyGIIc)${80dO@%Cv7kwno1ANCJSL80#P4NBAfB{h|`alMoerZWLX3g_e|#O0+ns zh!y0%7(clQ6>k3~=*ti3dmy%u^B88exGjaH%(`C^K3{C0(o)m;cu;8d(ZhUs3=y^j z5m(<*1;`gbWKiH9Rj9I|LG2i5AZ?s=Ns8-tn<&<9o(;O>P}8)F*-+Y^!&OhJmK6K% z0wKD=;uMC5!Fr9ORaKoxXwP^lM4paH5M0_%)Z!U;G$epjx7|Kaq4zpp+*%@Upd8UO zgIaTX4$L>7BJE9x%hDzODA^t7mvDmoEooMaY1Ysjwih86rak`qCfhln3dm%xuA0tY zU;@Z2P`x+qRqWsZ)WUE=ws)CA1iWaiY|PA&#k2C+T4JmtYFf~vZDb|K* z`<(${HV0V7WyT+?d`h{NL6H9nS!uFglSnpNv7?4WY?$ zL8Fn{XMQ*9(=!uxB&?)*f-J+XbOr6B68O2h&ON!Ht5hZfbHTYmxB)w{J_v>~e7q`ZE zvNe~i{{nl>I0en~G0e?m^~&7vUqe{GI3UmlFMoTuxyvkR5D~c^EPyR(KyU-34}WK9hdoiPWzQIeZmaIs(C=h3m|M1Ujt5FiuN zy5tO4pdyY=vk{Z9GRh0=UX6p3~n$x{G1_M-{KaiF31Xy<{XuRjPVY5JiN zMrnTEz#_=cr?6V?=_wn)_< z6{z|@!VJk4iK#N z&2D0M2WnjgrkOQ$Y2Wk~p`zu4SC@YFj#mwF4kzIzwqvc3#mivQD9#`XO|=TyR~}9L zK|Z;_Sgv_VTkj2_2!4xN0XyH}Lw@zI|+!e^m3n zE*qV;I;4K76N7AH5CJFiMW1?DW?*lgDf)C5K5h}eIyLSuwTjQ;@8#q57^FoM@JLb| zAPyp{U!$HK@?s<>j;G=Dhh)ypY~Y5V+5bq9Z`xI`jL1C2A+nMELlmT21u}ABhWWjA z;JM(JMBA!^8u?%yE*Cnikkl}HyWNsh;-2UQmcM8Tm?WQ7!KQuss$q5Vtn5!s(68cd zqv~FePgS^dUjGpU33%ae|Fag;lk=XmEa&O_n*U7&TbA6e#(O>S(>Xe!^Kflq}W)q(obU~-vf=l2 z@nRGBw9cTd;kmfLL1ARUhxPEhu9xCA!8fDJg7%d2v|=G?75UgdlNJv&WrV5j?9QkX zos3yTiB6G1J%Aw?ygz}~6aZQZ79y($jz+iF7c49I_*U-w3vW%P%5r9e-2T^2O+eAZ zBS>*QL@ZFNez*4a{OXBi9a(=#3OgLu2c_}=J4gI^*CUBspu|UCUw`nv!gQ|ADfe0P zf@-5XCwOm0dFz1`qqZBPdAOWMy)RyBHF@&f0yfMzptef*AG#jX|9Tw*8CL#WoV&!W?9j1RLCsceGg z1xqA8Ia~_b5VA|Hsb_RuymPUci{`mSR_tVjn6jKEk$NoWu58WvUN0-=#Rer?iWs!Z zvoxH~m2&F6Hl>O(ooLmgd_*ySDmSC^Q;q}}p$}knG*F4JZb1L>W;Vts1M@0X?mc!1 z!1+MAgtn4;{;#P^i0CKkvccp37h@*d!vD-%e~WmLq5qFWymx>`5BLy7G*n}d^(x*B z)~A$gu>uA&8jZRt!Y|&D#2lolc|anTN!ZIP4G@FMI^pbRmG8uy&p6HJpKE-xw(L_M#~c;DCET3XB=#neikZL%L*tZr{4IjX}PxJX+oRi2pNM z$w>BJ&jKjgHwgHD#hTfFUmqtN3DmcCIp9Kci+%I3BcIo-dqPmgUd4mJx%pfBi>-&* z!?SzU@MoKn9O1vB7iqkFk4S&DLmMWG-*_TR{R%Fzm5FLd=Q4%IlmH7_a+H2=z3~wH zp5teN3v4m{JA=MShWEC{ft7h8Bl{LC4U!r8t&I_bf@)dGR9~II%5&#$N79Y^XQFr| z7YojMC!Vck--&W9E;=+!txCGe<-ho8$_ys_ujF-91lP43((greDuTP9ulxtpVQ_2$`E<7hS3KyKW zo;8eq!F9Po;S3nA^f`<34iBuqa!>-b0FBLp%JU$m* z5ln)H+lJ~INcg_(>~_GIrHz#88%CqoIg#(VU#di$Z}T3GfBiTkWU=*FKXUOTa?w6Y zS-r6sN?vY6PA2gJ3U0NZL1bO1tK&}qo7l_L_6$G#iXNfq+&SMkFu?$rf_?3MWE{! zz#5|o^%=SM!ut!^%~V+ry$E63g47-LNnF^0{WEj+o%x|i3Fk!@3c?izG^v=Xy!q0s zNkA=Mizu;fT9^Un{rd8lA+r0phu{J4VfxGB!nZ)O$`)@MXid&p7gR>hA-=Y-gi%1ZTo0?0{NG?# zkd;$3UR;s>e4dhM=$2jzyDv23dPc}2$MN^6=3Qn=2}>UpQcbbPM5P)k>bLzHxKm-- z81$kNo^jm#W0LXak)$1$%&4b+z~)N6!$RP-UI)~xjMO>VDx=t%s{V;xLT}hw#)QHM zpAS=@k_9X#0?(?LaHf?j&Ums#$f74~^loH)U+L17i!DXNX$+7?M1deBxTbL60li9D zg#YuF3g-P{pbCRf8Dnau-o+tD%!;?{a#aCd{l{|Le3@;Fx3T}aHNNc#=D#{K|D-NH zRVmJs4~bIl*XPz3L`~j)&1rdnLZM~B1^1fRWrtb$@mAA^NMSI#g=P{30h}p~Rxv{4 z^EW~WcS%nLhpfA?=1lqWkt|8Y)#+%~9cyyb&Qa<9)XPF1{(oiA1ovjh=M|?johk?P zCbG%yt%hD~VBqC7+YbCM^)in?5sY|FBJd9Yi}($|*8T)AR>hwHjD!4t0b<04vMXV<}0&fmwtd5!grHUQJ zt24yS*wAm-i)S&3GCO+G?`la0sQUKjF8v@Ng+EL}n~z;dWKc|ssm{6jbO*^;S6k{+ zst3_!V3ev!e8`%06tjR~TiCkAjzjx(q2}Tm4my8@;k1l`&-b$sg;IRq8wa+JGIMycTN)@Vwd<5%`*CZYrPCV57@QG)P9JrkQ!vPmUNv|S-%lL!O(#2@ zNZrLOyW^0u3gxFo35mgLV=W(7jT6bd|Fz|uI1n5C%43#(?v-hMX-yx&zi^}00|%^o z9`m4vHc2p!RIid33E6aqEc_K-aSA&!HiA&o{F26;B_|=?&ri$n z;uR#-T+f$E=1|_tD#X+N)rzT~@xzK~hUovW@;_G0hu?p)VjjBc;{AnZ&*}0@bfMSs z^Xi8w^koWEUR5tYn4>~*4khLU4W&8gas7#DTNb}!TCxy_LcYf`2Y#024z%V1`*)&W z9?nqpAH(HN<-EIUq3axAbL2Qc(;+e5`AQtp^xq7ZTlpocDh38m)$5(*=`{B0qDjDd zHJbC*-~X(lYrEb0vx+Vz&M*? z8F;kIgNw!8C+zlg+F~@}9ehCbRMu#oVvpT_D(TK-G)r_Ix?LZ1j5N5pI0Fwcc@8`@ zXHB(KFNh5<24e+exZY5T;{f*N zw6c*Zp!MzD^*Z;e#xa?C!zLC4dF}+LmGgXm@@d1!;{P{KrBHt*<%?w+d`(x&N#etV za;&abJK8K*!I73OG#0-;#Oe|B1Lh*Q@EiA}=;WtovMoy7pXa;tfnqtV(Y@K)R&pOQ zFV1#t;ie`a&+&he_ts%muIu`!gn)u{sdPv;NP|d+bV`>ZAxh^!KnX!Ql@RIfZU#sS z(j^Vj-F@DnbFPW*xz=9$?ETy4oa@YM{4u-+bHo?#``phR53T?Cl&RjD=)~W7vCd!A#=rlX!aq0fJ+JQ;%cs`sx zP%n~6=@Vk3LGv`cKy2u1%Ve~J<^+zXK}2m2;zRvlpxR)t4CcV9;><34L5je}`uN|c zzhf!+2jkYy8ojO$q{LD!+8pK?idkYRNkx9#SOLCRc>l&pS81hR!PpY|l|+&20T_BiM@93(6< z;K$1(8hJL55gLb&EiKG;^a=&-Y{tDYBCMo@Q#Qc~!dQsq_bd3yV*B6Yy(~Ly=zgIS z;Kj-4LAy#laaJrN&3c&4Mx9P}@=LpkWzpdX9RhD0GA5wC3Y-WLNpuJsJBou`J4D?< zE|iXPrOm-)GM!4A)DKhK;6F@p2>JJZgI;F+6CUW**Z5-eM~nLmOZtpU zKe;{Q?3*${SP~%tsa!iLYPNo+a~UDJZU4a|pztD8Rf~LF|9&9M?1d&^bE(&^c1_{Xuu_ z|4W_Y=l@*ih=iv3t=JV^Gy0?TRNdZEa&QOT%@|~o-}o-HYX)kEKp=yaW-9f8mPzi}d;feOdEs7Df;bwhK9;An0|=5>(FU za{H7~7aN+swRrp{?~x{h;PKc&-rbXn*VywFA<|DiZS58R%BkOg=grmOk32{~ncwm+ z=rlu2*p-jxQv&;@DH~(cP?j3QGc$uazNU7pGNFHEi+fjKlh{ME2uLqJflny<-MJ-Z zy$|oCZIu~K8_s=x$D7q0Wd*u<(N%~vV%hr7?Z5u0C-HNF`FuTtmfm_IAj`xziu=c- zZ*fx2!dc)>J6nwwiQ|_tq_1F@-Vz=8K^vBM)(z%psdNjruB;ITil|CZoi~G@j_61B zqEd$!-$K>pH3`P z$;-9s*<&L+zv-|pq5ngvxcUBnR;dWk?5&)ppeD_0=z?`2GV21wpv4vF2Cu!V#$JzEweD$b)%&W7}dv zuzhk+VhO7U(KUN69_7|TSwGxnGb&5Xi+=M}hlQ`cf2b9qui(>c$-EFZ`PAclu`mi( zY49^tK=Fd0CXZisLffTi21S3TzMM$BMOTdT7&FM4Vw5(w=UTltK0Dk@ z#`Q*aJy;7xa;OkL$}#VL2Pp(41k9uPK2opo5Ub@QlD608oB)h=V*Yj-t9|~^{$}0K zvacT&8ut8tHSss0#BrPkAV5S%`0ol3rKl3R%*K@h7R6PVxldX?dH-xk@Wtn%*+=+` z`+ke)hqa8;`*qW&a?KQm3FJ`~uKl~3!%^UFrK6@!{dz!3JPgty7zSkYH{Fzm^JxVr zhdi$~vw*rr0=}FYt&Y`eR_KZd@Gq>`#sc51oGNopSAR!k-GF*kvy4Y&VRyxkX`&#% zlzD(Jf9!2)0*DT5YuP1nnue=PB{=w4uW~)$3o+38<>>}6t&Id}D;d1IK3~A$#q2g2 zgpf`Ef$?8EkkvPrY(+>{5XDEg_+nUa3821agMOvHQe*x>eQg8X<5SL-9VQlh&TQ>+ zAd4e{ncc9Eewf|%ceTmIJ@-KhqXMWaQh{`BL!GQDv)geTpCb)~wA|fLsE?6)^x94Kv^OgV0Kufa9jrU|QM3gH-BR^s9p3h|P{2ipa z@%138Rjc-5kc2wA0UFBBWltTIle_p%(%-F5{mF)j*gf6D!=1+-cGTHDd&w2+BQ0N8 z@A9oJXx`QInaBp$;k&CR>>~=+&)^cqDBAmNnuQtx{}FM;{y2-&-MV|mQ#W?F^JMZS zAGHY!ojNMqzq}exlK7v*lVI+eZ;fmY^DaNL5vC5@M@TQBH6PAn>IWJT;`^i*%ZI1u zJBbj&3J?uMU3gjD#qfPVrTEDgvyE?+B_5ZMmHzbLn#(~AeXNdD;9{#ydl^&@Y-OEx zU7Hd=(n*-1>Bu0DHj>`oBj*iSgiF)w8|*nc6X7>zp)mbugBQ)k`QcPLSkn8#6@=)Z zOKPLo)H0Z85@^f~tAtmChyN^^d!=U_1(kwAn!viw|wW(5V0aK$l@vtBamrYP~OOYYsx9cI3yUx~uTo>o;+a!~5HNbM+i5ryQxV4fhjntkPOs(%A%WP<6C~ zhwh;AW=B)Lo?`G{bt4U}z$mm-NZ~~;C%@cAosX|cAo_&FN2-%aKDwVV1l?+8M z0aVAa?zz`Aci`t3R1MmoQn6eXQQzRvkUsxsWY~VeAvYkye4zh?3{!e7z5&8nNXDpd z4wIaqcs~IwNuR90L#-Z*;OSa1=iliMDQ;{bE0_h72&U9ku{)fIXCs{4w06ia$bv`0 z{z<~zvdzvN)2?%?bYo`a+5Yc9`w5NSL7DeJqx}p>x|zr)4ZyY!F7h^XFMrC!KHb~!p-Tp^H$>1xv+uxo`Nchy00N0La zMG0{0XqF8ZnaYmdL$m+)5=z`bTkEyw>sAt1Ir;vjvC?cJ&6DHBM1;^gRhtN7g)TiE zQB1NziNOU131NVF$nD>VSKKF9NIp!?o%Mx^zeFIVkTl+|6}EdX$0l`yt=XCkf=muE zH0!4}EU12CxVhiydc^9kW#Z&+QW0+wg_YnaHTF(K_St-NXC8~}S=8r!Yw5a{>NavT zk3^JgU$gOSCB5&SN$70yIuVi=zjSJ8&tTra_Jt6=G@hATJBiqrM9fF`DB!n78lnDM zCQfHw*Ao;`Y9?2RcE9!{3X`_^6sdc0`cgjS6fX1e_O0+puv0d{@L?ADV=X&ph#weshC8>7pw>~EYLtjh)qon&yj zv0(^=*|3Knp_t}QO8>A#5nfxGsuuAedv{#H5wW4 znMvZ#bON8*UFfuTIUWMWUGC>yO4MbRs~~NAfSQN=@wrX5_m93>iGlk7FPfd01{x8M z;tCqG%$nVU6a)&A0tvW;VG#FgqEu~@b)h)4wJ{EhF7dtpwHzjYx_+~e=Pr`*^k`6u z;Nb8JNyao_CqrcecCwajia|jg_bOMvh(;IGCH_z9FVK4+O1OV-Cqr`o^3zV1&G!$G z_CIbX6V1MAC(HjezT}@_v_JDpX0s8qO(|7LCgp!}V>OW(D(lNZr!k$xjP%x9g4MQ8 zwuPVWBYIMwq>!J#XDvcC{opgG|-X7xdcq@Y5aFV33st43DFpu=yrJ<!-JbM2(JxUu6jwouV|cCf^BmMN@|!*6z>W8T8jtDNfgEKsl5@k3OP6e73P0 zZrC&h5fvbP9u?4WyErkNTTnKKRlA&~*{mL(c%^y|bI#%*S3N}m;3Nk+=#Rn1MiVmIy z479~|9{ZCsWnAKexf?492_#NEyK=)e!@Jex7AFIhs6L;ouTO4+$czeW-@4E0FDff5 zQ+)<}ySqX$p)sMG03#7QLgSlgTROdgQjHFw%K}TNK2D{VB@i955NpT!=6HFbCRhpW z#57=6s2jQzBEy$|5t{{h9v?TXx4;X9#`&7!r{x%v-%__JUu5+&#Sl@DC}52*J$=9~ zt*WEqJR;vccIe*c%no-WbC4~pgIJ{-J2-UW1zK))l&cz^K*bMu$CpZUg5pZnc22;;=^bKudU2B@(jL!>ONmNcVSNV(#OF@3OzP=Bi-pdv|FQ%5go9pc+UQCSz)9PiwdS;O0 zy!+ie^+nf&(9jR-nc7z8L;5?bCa2Fq=V>qqRnr~&ynX@ju0hhQR`Q-Z5LlQ(5!e&gYUCw1SrV2}^ zFWqqlV-@BbJm)n;9W1_rg!R&%s#6HXFzeRt?k$kk)N#&9BKsf-%|n?2^2Q_&|%W@;=r zl>e?)tdM%BWa0Cj_?Mq=o6~Za(Rz2q^K+AsPS(K$fdyQeA|jqOKm};t#z8Ud2I5Bg z%B-|YgHJ%EY0{S>4Z4m5l?zeYR4gnC3%elR0*p2&pJE159*tAkau6+LRfvSJP&%v^ z4*H^!EsdB)wNv~D6`unTnIH?~M3e2O8`TOc!}%;NEiLTsCw7gHp~nVWAaqX~NK2FK zJdtts+1i2qDXZ)i|9VOPz>w93zaxb}@^F3lYLx|}N1gMM#CwC(0)xrSa zhQpqZv!~+EnL*xbUoB>{{xrGvk@k#=EmhI8;AZ&@a^CuN3SOGQsHMrx)TM>s zmdAx^pRDdkKfmOl;xqajj8~PF+{Jr zAb9vB4ewS+J#R8gjT#7+Ig!x#%m_1o>XZm0NW*k(Iig#U$@W43<0~O4ANX+;U}d7KBh3P5B;0J@s8cdAP4V)Cv zEMf<(C%kizS)i=v+{Hu+q8tsDPJv)KG9A;^1A4l0UjkV2X+Y7aMluGIY%T!eEQpxe zRvyIsBnjlHWvRZ@kgn+vB{qy<(y!BTHdA_TXqW`6of04?%T6S~I>rJ|1|oG0&+3*D z`HZ(Y&kWmKf$1!=E{#*EN9W|%mj1-J8~-SgUgdx~=ot#jptyuWJXYS#mid)Lj1d{@aM9iAZ*g&@?K*{k z$wB3rQD-^rrqg`aXY5>!+WFYy*@vca45DY>RK(E~&F;`!F76TI^jq`bRv=9hz<2U)IKAcBiX~BS0D#$nBEZ9Uqm|R;3l3oe z7dl|T%+?+NGi@{sr{b$~?AK#Zj?Qiy5E)a!se5oshd|m_ zzGf&^5+ug2+RCg%?_YzTyY)PYOfNg!n(=e0ORs^qS*1&@>1 zX5}KuLTrn`lrOj}{hZPSCJbGi1SPH+@YE$5Kil81Yn`x%5bsih3gTIRTC1XL ztzI+u`;u6Nz{4B_Uzv@;{O9$9biBbMNHy=HpZkI|$Xn}-h5dJ>^u~Q`%;x15+PR|~ zN4^FR<5K{NA&Ww1O@yo4o88^Uhj;J~r0T>!p&9|0g`aB)=Z8zxlOQ&mm*-fz+40#qU? zU|En}T#$c7-o@I@YI&E1;g(QATAUDsKXq?tV`0jLDRll#J6xB zcX1zBa|dK$43}2aV;IBb<2eOl;puN5(E2Xf(Goy?O6B7D)NVzB7_RI=CSnEORSPs- zi3Aq~$8W7V&(oJGx?M#U!N`@!=O}tYX4ZM~fZ_rxoI002>1Rp_w3t0nK1abldkVfK zn|(Gvnps%UzRdnjvZOs9vd@rOO!78=4srRF(917e!x{V6?d}kH_mKw?tbV+R1!rRm zZKe8aqpT0wZ3-~F?7096D%q?!QXMxg20Tgkwvns&k=vS~kZC5akdU^G^(J$rv1}TE zNi0OCq^AMXZP~Q+>Sz}Sq^Z-f&gM$I7H5$wPZ~eSwu~vUzb58>yO1ZG7LE8l^qz>JI_mE(5dS7 zR+ZZg18PTC7JaoH|FPk<%X?FVWS`B6H=+8uBVgx(88&rIRi?_oVjZR&3hG>oNu zb-ZRs6_^xcBe&42h|!NNC;At*7?mZft3c}A#KmZV4*+oW$G_s238}*YH z3!M+^i`sV03?30*y`80|H7OLCq0_o{El7<{()YkMn8!M zgh@vgR%m{OL6|;3w(mbAUn5!%RI5Nk^(ypTzbv>3(UQ7f*qv6D5>Tpfn!0;B3>cZx zWW+iKr%YRbXi`-7|9~^}!N0;8nd2uEFR61hJvK=S%PuRB-R^am-o6UtmZ^UNn#O>0 z5FD2o*;8%5dJh?$s{T%q_BlS&;|%wqi9?)9AJ0VapVWYjKL^M#SKEl7Q|U-OGtT__ z#^&zkVGcoBX!G+Z?Jq-9X((4czx? zrx^+#?mq%nFe)%9DpdPp}yqae;wXDsw5P2u`@Vr!Ksv&JLkxO81|c1O?Wot>z|e) z^u>(z?43h#V`rego&GclewHS{zP1DML^Evrkt?RF&p?2DTC3*pn{{DYEZ3I9jM`S0 z4L?+>v77|4p>D+cz3h~{j3xjB6yTyegrM&N8>z2z>4MXma zjhEmFaY<5qIFMRqiMWewyMsd{PIs7+QZoO$*GE5gPr?tZm94E@{_09K(G~So9 zgZ$0dhovU%*a{{`N$&(dZ;ReV+W49z+A?{m+fuqXAPWeEdDv(PV7J!+;g%maJhmE< zz3f(p^EFO0=7>CCNOdM=>Yd@i(M<5)ARldyWmo!!i!BC>nPiD~$_YoX*z_meadZUw zZiROw^7}!jLd}46b^%_%-5+gK;?5dBOk18saJ!8Ii)}gjEjq;X11Px-ZtgOm9&S@0 zxpoWh52$R{eVjWjg#uzH1)#S+U<)T!gDI+DGZlv+%d?^Gf1bnGu_VSZVkt$9e2CTT zGjrko!_Cikts{VOCL`LBwQqvO-0i*8?G=dOv&yLps>oYt_EH<0t^sP)8aE~DY2K(r zOJ97H$Iez7g}hP?lgjN-=6N18&zaBCi#JYH<&ZAsQcf#Bz53T{v{EVDo+e3$1=QY}jgA^!Oa<+WKk_u5dy<23Y^KgLZwLnP4S*BUUk(fcgvA+xP(}4#VPMQzfGG-r zo&wdU4ir6)&mXkv4Q|ynWq*$VCJSk>Y)ATTez2}n64}z*98qFl#3kWpv_Xb8FzGEM z1+hMkV$shND4{JcC{To@VJirq+Xu#{c%T*&*=^D2zHj&z*Nqqoqkx9s{G0B)>X^*)VKl*SWBVZ@%#%- zs4Tck!62eRQ?3wAW^qVcpQcYZd=Nu`TyL56q?t-&J!HdS*#70A1&%_XQR87p_PHGd zG6kkfm}g>fYD!^FJ!|&1Tl(5qUT>Dbw`oVRE1^rFo~qMWvoC6FX*|*`Fq9Fr6GtFF zPR2HF!HkSbZSLBTx|>OHT^xI&NFhgUT=o6P4pRp|32$+s*s2F}L9;v#lqVki9Iu{P+I>Ugc1qq#ZS>M;^Q4iigsRp)+R=Nq zN>ZGwM0?Ixoy(mp8`bogYFDpTRh^dlr)R>>Kr0xdsJ>CNE6>$|)B`7HeGJ@R%p&Lg zEcL3J1VYsfkQehZlOKzz`#xVx=1(%Sol;Xb7bSW+p+Edomc%qQisV{nH9{aUDCpMw z_H7G27?C6_X~bpK!*9|V%)%5Is-I3(Z*;2l-qBu??tP6WMcItL2}ZUrVW-7ojTXRb z6_#zNNJ6geRjO{VBOP{ENiR)c{4VaqlS`hst*O^24GhQ_G(I#ez|X0A$LS&YmdmPb zm*`Y6(0`Nko!n@m2<=NgX4}Lg>2=+xlyrW=CV2oxQ3_Ec&?z{^kmvWZ_S z&IRBdQ&GX4UU)z~}WrMo|_%%yq zVwZ(-Iabsx#V@QzJqk&_^uG8B#mb3u^d}(MDmEmAx16|GCW3BlR&kXSH_Ka-Ia}TB zNDAeMq?yv3G!zwnT%9F$KoX%TINq=DSFr?#%|nk8QD)<<`nZmZB)N^mAvO@?ZUBeY zv#GjT8IZft&9};EDra2B=M!!`a#COjRdxC#+GfZzoUNfyFcGN@9AG;&7bmL|BGUTC zPA`>|LfUgX7+Yw$zPQF?U%oBAVjCqjO4^U1AEh~lN3y0EUg zBA=tK9iIyS5>L1Brz-w_#P|<8oTd7Tb@aW4Lng$tF~GJnPMrqC}1*E`S>iK4rl;!@3vh<-DkxY^O=Wst?pgq4sU~2YY6Pn zt!Y6#|MY_Q!S@wAO`XjlZEH1kp2rUC`yoN6O=tf2|{)ERKSJ1tL zv;z+J%Yw-UP|#(MG$^)W9*Kec6>^)6%EiygMxK&28I88&5l%d<0qb}(yM?~T5vj^Y zN0#liwbk=MOtC60mq5r9c=}7K(K_vIz;w0j z4oN>Bmr&l?kXgtargQHsro?@uM{&$OR&{7a-E!B1C^V&iN9r#SL#4=eL-Rn#5{w86 zG=s}0_!s=sZcHdkjYsCqz&nI2U<8B;QrR$t8^ko_lRhEw+0QbhztfpCnQ*TYf6+b8 zx0gK9PVZ;2m~;VIe^o$sqq8d_pH4yL^mxI)7guwXgvGx|w;-&#RwGXH0CUd-MD=S` zV^D>QwQxm(sQw)HDVlg1oU03)35{op!0NJcW2d6zWZ;>2pv!u!ei`GoRAwR2OFXJb`3|5fQprtkKj_UTBDKehx=AxqP|VH z_|a#h9_18gGCzOb?a%-@7%{?f?Nll9eee_DxxV-2`fJVd{CXAOjr|7ja&!Ix@OXy) z4&Ws*e!u%vE*2NnE7T7FygM#`0K5t<{&Bc!8iR|PN!tnH;ms;gNtqua?^)Ems+Cle*Op4%R2iN>QxTX^?ETFww$;g`jwuy3QprZ>U6pa^+rX>j6d_0J@+O!P4XpC z2EiSxLrhF0l|NA)lpio1IO&?t z2fSiVp&wiF1ztlbodAm>4Tz`ft`ES5psNQtHjP&`F!_tl8pixPI_o_kNPJoUL65}q zJF2^OYJf<;p*&q}`Ye6^z13j_=IS-}%8jbk*=>+hti-io1jgift`jKC^Reys6r8+y z6us=gU5db}Yu77o1HHv9MTH&ead|ogbH)(VQnDl=)HwpprOCma)vB>m44G!VZ9((R z!UXnv^G@lANJovG$r*O$(jYY(&SRiN(s=p#0~j@@3G z`0n7o)f1h`*!)R8^JirN^Kq)$pIqysKSc}UTePVAmp!AZJnsov@(Wu`puflp z!XCf$*1SCHDi>%Dh<~hLV|`YsS;`>W+q7RH{?~l~_U@Y~uYV+Wd=IQs-C-$ww;%aE zw+r;z(-fJsikfk5Luw?GmG6>2UQ;Aec= z#)~~sqj8h;0}~cM#?32T+J;^cvXZBQtrvxNc=NJw<#cd)ex~ZPlsAOr>r@uOOxEV8 z1>o`$yC)Yz2IehBfcV4gC0L^N4v0$R6>uaxsdL#kUx(jj^Z zyg?am)3uX@$tK;)ATu(ewG_;;-=O!`aL%Aa%K>Tl3>ZuuOpJ^Xz|+yuuOLj^gW%dW zCw^@niXj|0#SOAM0;w8EeQFNZ$2FEMWN=_m;$z;{r>g4#zBA$v6#+?H(*)Ei3)++5 z=4LXB23na62Z_^3yZ%R6=D>1m?h9_Y4lvLsV+_**yV;1Xg`GBD+uA)|3RatKy>?24 zF5!eDmWqO^cL2RA$O+4$!<%=UY9}Hgjg14 z)z6je%{_uGddoXvg+h8PEfyCvL%aw3C%V`cQ(~#*bp^X@5d zUYi|8*DS!yju72yl8h#fkf+`{l1lMdVGq$MPm~tc4b-TzGsAhhXk1L%Crbv>-#+1`_LbC(Y^h4KZR z`eEJIOowCn$U1g?vD7@$DP!Vg@xs{SDy_Fxo291tH?0k0s%kTI=KIRbXSfI0hKf9! z?+kFN6a4Riv?qb>nPJqyk_X6&aE5hk z-#ylEh_zM((NspnK#sQLTjbT}JV+hB9>n*43~puwPvlT{;1&y^*I%H_kWHdSwb7Bs zq8ZAoXPJ+Z7%bS8&KTL$`c!sfeS+H2vONNoaOREir7?GwsQ0G~O}3+Pj|i`)-!sJ* za<(b-czU8VtE) z)a&z;F-kSDbCe=T03hL53-Ck+qkN4r*v1I}C)0RWTYJA(Hk_mcb@9EN?yy+!}DHqaM z!@D6qe0{HB`0$4WhjrO&tg)E`NCWv__cp4wzQy(V@6c}06@vQ{y$ykUSAipVj)+4W zZMfAqXoPmsA>lr zvK$+$6_|YkDG~EulU_i5y(=QQS6NSBNqSoVwJ}SlT1S*`h*&|lx-fxeF!nU}DH50L zUv;P_J9S7X@2X0goRW+Io>}dyF%a07r2YzMZPF3&YG&UuAdagotBoMwq+rfuW#=hN z^n>v_?;kR!h8r0cfliGqkV6d^JzI9eQmjf@tM_6HusSQSanh1C) zNMtuQT`eqvbm9HAI+275Ay#YDZRYK@iLU;0YsW-WWMOUDx=Z3`nqkVt572lO4t8CTGPv%+r28Qz} z2Y0zkvFZo_*;L~OKAS#wW0OHWM{!t>G;OmN=nredwi50YTz(W=!<$MzAX%-XWDYSa zcEQ#vS1EY(*kp@0tbfNTbQ3jo>x)9mp zME;gzl!Z9vQr*_oQr*&})K!PBbS}rnh3b}(W*;QN#xK$QgO0XuQ)}2qj9id5pDaGD zTS$6esIn@(@lKc^+rgCXaRo^^?Nv-K3FrAWyV1CQDy}zL=7PGcu+B!C>}^W`fh_2O zesh|8C)xa_H$E|u1q6}2hGjl6*Bu#^`W_&Yx_v~8-d`Qg7K}4&p2k3uc=@t0&i!CC zb;nCd8}L4!GhR-;W~vEpJJMW|E?{h$(Iy>fus#^I>#2ldO!zs>Rr5f><3!~)o6&2x zopu^eA3k<*$cXSo3xU3>YnrI97@}=NVb2LjCm7t?0&Rm%fQyh`F&((T_vPvGJ1?h3 z&jVX#G{{nEF5WZ751zI)`l*LWr?tP!qRqNLlbN1C-$LpM;bYV+c;Flt&*Jze+va?d zs^aN`p_15^0?D8_83K|$RVxs%lzGAcrXgj%D`@4rDEU~QoEX!*C&->^9OM1qG<@Lt zKc(3&`qcjyXg1?3Xe{EZfIMOM<;ie;iU!MRYnG^i0aVee7cH3XAcXlRY|7Hqha9I? zA?I^+ZzFrcVIZJ;9dAC3w~snodnz0b>_bw(`WMlQZj7RBBDnWelwJ=^mL62{FsOGV zl-W`i0veo?vMl62My(>T&q2g@>q+h4p(K;1k|fSzMujn8fG;<#{~hvcqC{1&yc~L= zn%&2+xIO2?3CBSFJq@u|3Y~i5m|)~Z*-9|RA?qOQNdxwLGwI>aC|@MQUqWy z{LSGCbaTpPY4x)@$@cHIIW0XvmN@^eUU`SvoXPidm-FAQ(aUv&Anz~yR^>W^y_YE8jNls~g+F>jbJo}vxRfbL**J{fo2&xvFZQpBu zrYU=KF&EF`x&L+?3ObsgFZbPmif7M2pe&!&NI}vx27EG}M74CmHKM=@a7pXmB5##_ z1x62b0Qo^~&5l&Sn=Y~~)!FN>`ip~3Q3*&c3IkDn2eQ;;-@!L{>9)7QWQo1oeb@9} z62{yEL+Ao`+Kj2UfSxxigOEK>-1W?`;UOsIaE74)T43p&Wnr@;;n|qdFB(z(9jV~~ z>2Pk4Up95d1S~#h8OF8+MKi+KHa?F-tX(L1nov^bgBr0h7QAyspG3scP<_5h1!2#o zrv!eg9e*ko!5GZHQ!Jk8_~ZR8n>HDie4ZJ2#z%dOH{R1%lg%&FJKus`cPjR4=pzDkLpR60i zU(SbF%_rN`O4 zHRxp`Tw#4Z!{j>?V0cIzgR?@LP1WD>@bejQ6ks`A4NptsQYpw-U@OxUD5ZxELaK_}Gm z#KMn(20IMxR(kg>pH1Z8EaSuuC=B;+E)7dKEq?{fVB-=-!8UgJ24B$dp_K2o**ycM z3C5PP!?7ZbH|0Z{VKM&zY$<`ImSlxUT<>BVPKo(dsY3N51Qd*XgNGiEGk9pejj{@@ z`A`)x@8JZg$rP~Y5m|^2-g`#wMZWEh?W(@K<3#mmDGQS->%sWnp`qH2aTV4zrb34p z-^>rHdcgFOY2Z`<_E<44mb=+`Q=m(w3{)&maazSs+JlJs2jJ~ch9v|8dC^|_wi4Oe z7!EWldK#ef1)c)=1j8JXPjSL#9*2*JHFgc+1Bhk}BbMhPHtLwJ^FpL~#4Es}m1xqU z2Y4sR$1q?R<3qn~R;@DIV+5>;#O>Q8@I$>~G6Qu;{Ucxwq3`tc5dVWF0o$cDOBFGc zKDhOu_47T|Yw9GYCgUNa6xg@M30GoxBot?3JDZN#jDAXhjq{(@ekkRnUcXGq<#6JZC z+6CK;(h9^@ZXnpM4&^2&L6=?yHl^*Z;Ebjv>fe3#YJ}|?O9AbI>@|kUai~jkk?%(u zSR&$>J6S9heO{&jNJLye64bcfL{aVbpUA?c) zUyWSS_6%dL|FmOer6(o@asB{)nF>7nP^nk-7yJ2j=7#ClEIE*+%ON6GVxkk}TI_fJ z6UAe8;Qm?*t`3LgBOm7tJS`Z__QRE>cVcMQ4BD+Z$wV*FPQzyW0-e0CD6D>bX&uBO zbHbYJ=QUR^)juSgp2lGhlu^5g!(V4`UTF{i=|D4yx%O@!4dLm_t3~A>&oRYbeRjbl z_X+>=j{(;EKPvm9hBBc4D1_#gil@Sy$w_~WKCxQFg? znqfAR<<7)1vt;ays*~oQ3;_iL*xcvvBGe9>hKealMASW>m|k77!E>tePwY#;kc)Qr zzMNWpMurp+GExN#?D(xs)@o1iujUk+022_le?(2N6BvE85UHUT3LNgMzQV z%W|p>5KFg#;YbUBol!0KwtFxNe3KuRz?`ut*tS7mt1qa)GlS?1X1vTHu^xSMKb+VB z0TjzCP9%>`$UURcacJZt!0zY#og0{#oI#Ii*6JhWBS5r$4kl%BKRt+K$gfcb!`qU) zZr;2(KU{xNinx%u`;rv&of!Zp90_)X7VjU--b7Vt$8bKgnaUm>%1HyLAXSB9K>%RY zqJ{$ENog;DiOu;6aetr^fYAMIGhlg32LM~dfVGLIWUK-%e-jOg+6a*+ZcLfcqw+h2g zCgEXD%UeLoq&K)a>AI=rJZFls;xEjEiqGgb1$8G}34x&{5TP<4i$<6SCby{_;u9bh61N&$!)RtJ7r;RC;HtrD zM{D~W-RiQP6AKL9=PrZd;$^~$8?fQYFih_5Rj)$;h_ z`e69XZ`a5SN(70q)gqfwGYaoU#oR!MjfqyO3|=K50p|Dm6{3OLgD=^z{`iYE_K-Qy zH+D?>kmm@_bQ?~AiDJqL`yk~Ao(lF?2uxtqdVVLH$NFBJ}7Qmay zW}Nxlx|`*sw?KaFv#`LP$LaVK%yI^wzXbYB&SI1D#BtEQU_Y~|t!x^B_7E^bBGb4^ z2z#(GTLA6Q8t~u(DA1$r9kM&`vZ5J*S~>3w{KP;7LqAEm9YmiaK;oo=>t+XBUDCjv zs*27tJgY`PoEAK0@s5w8hjkxq1qZU8zB+B+wdcqYyP2rr?i~EO*jEh* zKxY8&BNuUYi`g&;MZ-CWqjDY-&IS?O+RAu1T{s9-(m8=IyR2G`o;;0v>2uQ>;39*d zL*V~aRV4(|(_siVUytwEbgwE2Hgp_=at9RGYL+wS2ROOj`W1DX8VYjAUu`$B2=t83 zQq7&HlfCnjg=WQ31U61EhB5cj0ne93cax=N%j>;He*bGhFu74~4>h2;M@W0i<})3{ zUWBMdtbr9$)7Cg}6Wl$2{=A!e$886ji3+98AAb97h@O;z_Vb=IV`E?6D6xS8^(bUT z&X+nFM_>hz@0YU@QF;|<&LH)bHh#MHvzJ+Vf=kvJn6RPfBk0p?ti;BoIgMsBsHU;3 z{W{3CNbXsK-Bi_y0hHsWF0XDJ?SH1n$7cn@w3-y=h`SiOHD)PPM4~fHj z8+9ANsK*@q1!w^2nbl;CmiRFc9}iG?f__@=`+RiD(LD-ieEu+(1eL*SM7_3;?`9eu z-hz7_x&Jz>3%6f{FzL)>xN>`dCZt^Bbea7;X!PuSH=ASgw~d-gVH(Bw94IA}kOogT z>sj8sd1Kzon3H!)!8i>XriA>NDo_=TW!Q&1kc$bpaFr7#xGaV;un<_kvkKgS&J{Kg z;V0YP#OJRDOtI|ky6zW?m6-yB?J=?#!Mxwuj32|z6VxBQ;Xt+D7?|;Uka%~)#i;SV z6u1)xuy*$`hn3k~HCEMCNrL)-vN=28`@NXf^~WGnS~#nFa7!`n8>o zm%pY+<8bAW-q?N!x6R7lKnH#|AcR$ji#ryy^iWla$!|R*%0a`eLQ~8hL;T8y%X5kGF2| z8Jm@5`D&eFow%ZcrnU|PuNkg zFr81+fD;kXZBG_0{`2NiD(On5^RnGzWcS?Q$29Km=6Dz5k}viIQ{J?ao+d(*(M5>C z-4Z}p)xx0QNVLdR_7sXrm{U8ApI_xv=-CKq!PCef-%fKmDaBHcI$Xus;Hw<(AzW6BNyZS@Xg!Ts5K8hTY|F5;-{*q02jRCkD?2zAqZP7FyytRb_)P zw%+?Up%jrWPgD|7lKK1ps%L+cs3r*wO4d0~N$6z%u8tl+Au4RpF&|~)`kT5omVeu4VTByo7uajJX^SbZAjv<=b5&4CD@2m@|90=fzS@KLu-7;gpZr~sor(Ri zFo9kqjplFOlM@HL_T*z%(68&>53Ha}{lHjI%}Y+fy=%`Z%m0bn2t zF*f0E-m^~*y!PIco&O5G@#oi)iUa^d^#;4(-@Runc#RXm)Zw>F8TQW*dbm5Qnb;;S zfA^jo;I(H+9c#ap$v+OZ8a(Ha3U%Aw-@WG(cwUe=?!o%SnWTR-9uN|JCC^kwtL!Z# zcucBl0%;?C9@bRYt&gd}rmR0ra89$Fs?!CuNcC%(70^lpgoY$K5zh;;vjS(5(9oWD zz+CWuHFxI!P_|+JFIlsOEM-a7kg-I?Xo!leHwrB?BuS#k#8^fsOO&z;L-sACg=8mt zCd=4&S!TpAjAaB;6@09`1)(guf_tRpF?+FeVrA7m(VtJBb*7nWG9a3C%G$?B~ z|8xuTsk>ygv0m?FlU|*41IMocs6>L%4VMY_tsu_j&clHi`}i1o>y7f#2us}zzN2ZL zG98DAZHBD357nDR2q%B-?z#VDb(w1LX?!JKil0Q7o&{w`>PqMyk<>h1G~(e5>k z@$_S;)C6E1k7t!1*~?Z8kkINObA@ zD9B!9ivj}Zfb3#<697wOg=Uvtfg65VzAX|s3qb*iJ~S6P&q4acV;mgyBO9LOt4Ua?+A>_~dlxQkX)yo=e#TW2o*?647t>DoA@GhR(AP z9MGYi8~_sVPi+Kjr%Zivx_*0e<(V(b0V<`iwP7k9coE$eKJ@KB&v}~`e)fdX2~h`y zU6-l8JDao9;67UD^Ej@g&OXkCUWXN>=PG0ob^6svdHaYjf6KRDXsNSERMrU1Z0;;a z_*pcPK}UtuFH!^yer98QpZyZ;{P{|JvK$C&XAi1L4Q$#3t!6JkX4MKRIRzEL%%4Hj z8skbA$akIsI`LgK6ip7cOxXecLu>zBPp&!iJ>cb(xZ}-`&UvgyX%kmL>J$?6b`|`2 zjyt&5M<#6s_XNoX2AkK29eUOZQiEK!-Pbz7tt`?>scHRAjC%j4-)r2E;O!!dwJ;fD zTi9cMecQbzEDl6_pDjLmEhmrE3(e2xWm zjs`?kJTSG(Z#!xyrgJ7p%$>HC#YPwX&#hI)J+b|LaZ9}5pwmA*4{}2D^OE6XGz0{0 z0t7JT7d)etO8G@RlBPK6rr6KZha)^oxUzpWIpx$j;qo#oT50J5BSz#(xK=gv(%F;< z07cG6RRIXa>!<#kZ6HafEybGhIeE|wEobjj@3a9hPPs!_UPtM|%{__9T>!LiUskvA z>f}8PlvevG1L?!^ZJKv4?{G!-QlVYQ{iBnYM7kJo-O;6%*A3Ojr`afU-)YcJT3b7M z?Oh5=p>ak5P+SrQIZ+##-J4skofGh;ROoUVQ@XYdG#vuc@2_%+8$d7=jhJ&S0n<6P zcLU|Gew5$QRQ?dCa=55!p3Ngg9IVHbA{RUQy*I&>_{iHRS#54n^ccQG7E9cEaq+O^ zYlZa4KbE8nojzxvD99g^08Zo%l0d6;oRzzjcT-*{KV4@$xj~dZ=uG&-X7J4AXz4f! zS|8HHMFC+nQC9v(6D^dr`f`}A-V=--BS3Q$WFx6>fwj%|5G*KHdPYP?asOUAP6hWK z405;|<1LW;LLEuSvFndeB=^_@bBeU|1qN^Y5 zh#2{{6j5I!9811uyLYcPiokEu%t+-6q8Skv5}neh?I-s|Jkq6bN218rBYsSlDh7*o zn|^Z7w9+ydCdrI+7$!m4&O{Ui?Gtos|H6=822uH_3}fK0Xsxq4@~Hq zii3#uY}Uj3S!BZJj{EDU&zsvEe_X+$k^Y`Babom6rLZf3|ylhD|1ahv?JORH@v_f-O+GPF8s^(H^|#G4L03yUKT>% zT%6Sru7ENZY6ZzKBL%N6>2=~mV24g=LY49NjnZnWk?%d&&c>8kr_r?SEL}x$;36Cw zU?%uUVY{k3i*(I88UV?^toq7?smWeag8vbkYw6L&YI>;SmHMqvf}`hVO&4|Bf`dgI zN4zi8w}r-J*oNC*+$P+Rg~fQ?cwY;o6~lq-)~;8j^Lb%7kV~@f4KxxG|0K)M7?7T|8ngZ@R|3G38TMUf(#6#4{Z^e22%efFu$QMOn#*g z-&7u-r0HvfdtkCR35b%Htd3Swa78z1d^wQf@6X>S2Y*!q4j2WaM75YVS4(3#Wa{Or ziGh~a%O5Vt(k39#I*`ENR!lE3!V+bl^Kh=9k{}ps00#2egW|Q@c#Z!za?8y+1klfZ z2F<{1t72Bb?_^#pg|QuKwt$~wZ(G|HP9Qk^;5qn#$-7TuXzPRJ8$b**8vuIfmEAb6 zn5a4M7OqvIS)jV$EJp&FYg9=G`*E?^9}2h;F6{R z0ds-;xjl%wv0n!&u(7$h*&$*7P-NA2DX*;|l;@IfmSIBVninc2ZtZN>i`SN#ZIK3t z@~+vwTe=~9S@7dOR&EDjBQy#MC=zw0U*P-%Y7#Ue3Ms;GTc=-I7Cm(9N?_{l1e(l} z*a4}&TanT+i_9XoH9BaYo3XyU5a(Qd)g0H|ZPoPcil^%lfjU}8ndj-E3er>3iBgc- z*n}JZLTB)Do=hHoIOI$MTJkl3&-gxG8%P37kQSgx=T>w%55E6r)Q2tE2V^DRfqwRn zwW?#6ZV;H=1Lz8AIHaZ4<%jewZeEd4zDF&P=IIl!-Gi(#9k zWk9pPGN3R;zn$&WyT0`ZAO}vn!T>r@;?T3$Jn5j@HR7C7OXDfvymlj2uK)Dakm>_% z*M1BTn)VUGCTKIxKED(sRe*<|3FZ@A@cdY(ykcJXZxRK)bOkhoiU8&G0*}-CgXcxY z?YZa|c7SBKe^6jD`!kS=70D*Bk$)C*{z<>NNt(`dL-u7`j8B9qQr`i*gInA2(j2u5 zYC!)lZ)IQDfA2j)pejsY@$t^yT8~*;X>4{w5iin%olGb*HXLIzzkY2Xx_s3s*z6@# z0UWD?wc`03$HPOGz1IH7R$V(!f(95G;7|>{Ye)`vm~ox71L7BzXi85)YTx|KCM-nmp=-_TA>IM{>O z9+bvyZHxqtY2@%{EsyUW4o61Cud0x>&Ha?79rIZ0w$TooR7j9C3BVC(1v-|jZCeYP zZop9O#|MGSXPvp-#R-D9Q9xX@z#tH3H4UpnxfZ2u2b))Sg?HI2Js)+y{h6V35czUDt!-gAZaG# zLU~NO*K4C4`W2(lXe7&>;9{gU3jHuC6nqqg&T_}^mlIrx(RC3R46z;ijfME=vDwrG z$mr$W_3GVAK_pr}005Gg9hJWS053(la;wrc@1^ltH!w#ozZh(8|7ZuhvWt(LF5qV5 zs|fqu2&sDFRL%`&@94ScD(OOXhQ^AgN(E3zQ6G3*!_Z@+!%r zzDa-yp9dl2WCCu#YkgnCA&BTwHbZ&3pX1(>kP&2AUuO?sUo?eberCB3Jq4do=J=U1 z>4f=hDYG=ECIv^sK01HPk&u4Pk(w<)X}RThy&49rzn^hxSN zFN}vzAwdG&!vRO(z0qUo46J-P{9vFDvg@DBf0OlvI1c|TuSS)XQ%cjJbAXF$0b`7n zw?4{$?Y{Q*reJJ#ZbdQl!xdWnhMw(1Mja1oLzC6802FmiU>N2V9-jH)c0+7!`h;I_ zXu_C^Tu$}en(MlXFAOJszITEXq8r$dk&?^#iV_C;l6?)U-)-;kPk9LJ^iemlewW27 zSKq_fAWz~csrs;fdbu|p-W+A}p*d$;y?2|%BlP6? zqN;qiWbIgkPQxw6wHHpF-AnfOXXIRcfYr>tNU{sE#WnK;sjohm5sbBdbmwUwE$vRL zy~>aPMU~LMy?kKbnF@DKy~zQXQF;CC^J9TFgH6^B$&#?G0W9~GcDcK|y47b>zUXl$ zX>27NV?#Eq?-7ARmMJcEIVKq?sA>LHUq##(IheVI=NFH$6*+8?@GyR)4?QMcp;hm7 zaOsq`qBPssRp|N=G~ObWI$>!dyoh9L<$>&1^kT-+^<)Ca*z1%)nK28v73K=0BL3PZ zx6CRFhy>WK!ohZhrmY7gLUSx$8l%6qp9b7NinW z7{4z%P|*E$fGYF7$d8)ubu1{Z%&R8~f!3xZP*M?oEUn!|3T4D~4Y@f#CHSHpy(mU* zW8@LBP_61kk)>#ZZxot2xU9MqbZS^&ED_37DcRiDyC789PZ3?TykV*ip_)zP&cD2S z*!GotMYB~LZKNG;K*D|18$RFA(j$3yh*1*}KuIWcN^440Bh5R6e!n+~$04{|cw8e< z9~tq%+ZD8>q{lA-C%1nbd!wx`!M%7I%w7^3ozdEJ2yiKdktcj>iqG4jTH=-LqGttl ziWQ+E0b;{kP-Ys_WEvHu^xO|aYxm8}t>jjtA3d=0Q#8s}HTXIZ^lI!tk11FTkxFw4BXcY5ZE; z{#<4lxo;&>>C#@#`<~lN#ZNKm>2D=Dme43Cw9_&EgVLPYO=J7;q4 zRN9`VXYT7J7kXbCSGqUlaD*(fmfw3Ud*q9*J&uVwD3((W)H2?yrE22<{re8jwBbvkY5ptjagEl^Th@{0Iiis!NqaHSmhevJ0ue_2@f zB?}VAs>i_owD`m0;DX-;E)|&ta(kVxxu3SDH8WhsR-IRf#nW0;;(wpGg>?0pb=K|| ztM~1jhZ&zAFywSsQ2v2qTAu96NGZ(T@=kn;)}BU@C8@h6yJOwEOh~F>Q^{>{bCk9% zoRWYZqMy#Lk8%{R@OeB}emgs{Lk?%Bj8vkIvgZZkH@SPT0i=h)Ok`9wdZt5qz-i=s zOaGmgSpN-eir90+b;E^6?gK|c=PY~4Z1x3H3r-~getSGW&-1n8YJA)MYU_DdSVTaR zV3}B0>G}BdeZ;il%?{Jdmwj*1rzIrU6cFZ<CiACI~CaOVY!%nl)r{FVR6h-}n}Z&r?9l(f2-V5}EzM zA`Vgaeadi(e3!k86Hg_8Uf2$072iddA>GxL$;b{bZ;rqV4!HVnwh~){FIz0=bvSHK z5hl7kHD-Zp_jtkhR~n(lz&`At_455k>pU>yW&1}pS`9-s?I-(y6`y`EljFEofbrSslR4|a z@s*(Gx_k=WZpt6dTf@pR7fgCDQOBm9+UAbs|0Y^hrMOHxyYH2Q9 zY^-hlRNte0_dKF5Y(LvV!0YG1#arw}mQ*u*%EkeI+iP%$tf4KOtT+;g&Rtb8AJw*% zIUIK4HRP<}0#|h0vEd?NLr3@czIA5XCAi&t4vG{d*n44rwYxf^yYuV0Ce@$%8JVrt zg9l#p>A`GPdv_5)<=(kI+*I-%uV4TKx8VB_jJoi1us#T%Z4($YndDk`IpopEMnQ)h z*jJ^Hgy*wJHUK4ZT-7WppDVU_-m=CLJ>ADdr5MPzgCLfH3gNghiDN<@0wGk{R{T?R zv)lj)#Hz!{2Z-#h^R~Oe(uoR}FfPfi&aFJgGT}?L_Y4Z=?*scrOzY7()MZ&WI(`6F z?E0vUjY~z(p!u{w!eyj>BpCtIvHBR=_fAz07R8~xU~}>6W!uG%M!GPF0=^(6+d-H$ z0WW5{VH*OQJu}}iF_II$HJBeDa5><~xXraG&c@@HEgNA6*doe6x!pp!K$o)e~<@dQLl^TrmN```tz-TXv~qyI(9vnuc7nZ}U+^L`6{ z$StqhlbUzW#|gS`4~^|)u@Px$mW2h6(+`P9aVl1dUq%+yc}f>4RropM^db;6$ke-Y zghy&2YKCt3*ak|(?1X;VTq>a(V=bUQI=HRMRIONW?-8K?eCv%@PqUorD<|9s?e?&4 zM{Z3<9K&X+^J{*}(W-d?ZM!>6<^w=RTNsaX__ofB|Hc7YMU8xFb%$9mI^;=r=~R@( zS9R+J&ZeM)jubAY*m^SJxq_q#0ur{K1Sk^G+jr`fRWPstuHO--#hGBkI5_PWJQGAw z|I)y=JiI`tSR+6>Dgrx3kStG$@;Cxi;j5@zB&+d&Xc{k zm?~;&zOwgG2Bx;DH@Y}CwJYL-Iw5lj)0biuhE@~xE_Ka{$EcCEL>J91wm%+A?!9oK zw~zKy?)Pk-A|{X;h}Ta&9v}W@Zi&3oz0$r~(Tk6IW6=Nc914z&)1?S@d4bTlJkj$N zMX;3=bp+!QyXaKy!}E!VJ`bq0=*1GSw=TTU6RvAuxe)}d98}YTUFSc^4`StXd#uMX zYKkgOh(|BtwC~ zH9=3;$rGoNmyHVE?imEG^rX(V)nSwM;wQ0}cG<3`q`J4Py-VG$3vwtI-X;jHYF_kE zuim~7%Tbtf+He>rC$ZIcTfj}L)$j+T+AZP~H^q!hF#y4&Ots3cYo9-D^>W7}-czhS z!rHX_DK{|S<#IBT`KzHkwv%KHR=nYESS5*_5GVx{;s>Lw=?K~B1T@d-`DrMmP0cMf z>iLQyG_X~5I(JKe!YpKOC1V}dw_+IlMDrDaJImRKte2UR;J)51mXne|cx7aZrr4Vc zo97y!@UwVplXEY1yUN*-8MUU&F#ou|?|7VuRP`!)z1uk_U8wZ+qWq9SFl>8*^Ufu2 zHn}=9HvV&DPJFx=Ck*h}(d~-T+}@|8#rvi9=I82oR4W@5s5s^%x|^JLa2PHjq;x2o zX_{oFWM27*@TxvD#}AuBn`&%ia#BPxHIfnNGEocuB8QL;2%CvH3ecG;65c0pk^>d) z+o@0^C{hG|0oI$VsG`YKNl0k*P!9y^JtYm%pA@9>t0blAg;HSJlk$%^MW9SMhv3XDE>z7lz$o!T&pjX$H^VoW#Ci!F)bb~;Bla4z*ImDsG{ zR$x@l98a++Z?!pdFmVqkjbQH4heMcG|8>yZJO;kfdQh=| z??0k*&0B*`d`0Jvl;lxckgYjj{oP5`P;<-b>FUZA@}ruu0Si6<^alMufOw0 z!T+b+F7z2F+@hbIO}_qDQZI=HU&+O8UIfVE{*OQXM;Tq0!J5{#LQe>uF16ljX{ zr9>__M=>jm%Pk`Z@2@DwoIDkEE1C8PGtTw7q#Wg9l@L$t6anx^@+6CI^ zJgd-1CHy)7sOq`6;Ik?>XA)^D255s;ymT3Kx!wik7ZKF=0|THdsKTd+1m>;C039o= z$rZyUpr}3^B(&m zsc@4m;iA~di;+pd>~HH3NgwG-!ptQ_z!d2~o;fBp`Ybleu?* zcar?5_?8b90HAciq?jT`jc{*)jPb=FH)Lsf)y|zecTRYBB%LmKxNzl}of#t!S8QBn z;D_X@APho;>f?503k+fC(HA#VurZ@=^unmUK~oMzcDHU?*-h@Ll{UDW^rVq>u>R&H z;2e2>-4Im#)C_@R{jogz8S57Zx+g3bmd5U&svk?<BPW zvkrV?V*3nSUOhKv<~f7T5I{*e`*LF8b8F~GRktg`PbSd8k@0pp$KpHpr5LOi8nw#W z5?W=t3#|Di&O>U+rUIaMBkN0ij9hjM_Qpb#q*_NwqXA7O0ZyFqI}FqmP4COVdVMR+ zv&n~VKI4Xj*>PfANPPPr#~+0Yoi+n7Z1q^d-Hl}(_F*tI zqXhuFoQ{iL(0Q#EWo&jp;JWA;pPvm404{}d3OyPkUSmaWNlVzR7cDqjv!BjTFX@zP zTG~!4PBTlC?X2g!S!@afWJ2tB5_`og6tOF;xUXA6ikG>*^$~-hZWOERtq1m@&)&IT z|4i(En|{6sQSG8HX_+FAa+h(Ob2!^c#JG(@&Y%+hoLA)+sTJxV+!hAXU^&nxW^SUq z=3|FYjV1FX$6Z(k^Kn{ewz>7i5fXLFV*m{Lz}x{{FZ}*}1L(2~j~ql8Pe9kkzm?t_ z%g~(LU0b+%-KE-h>!EeIyJKXqUe4DmPs~onU#sczcueD60Z?@mw{?lc6WMSZqPmm= zmn@#>;f^J6@_@NKLIzn~(E_={gBl*=1vEEmNz@@XA_=1OuETIc79;x!Z z%6Yqf?}Jmk9Po1jD^_&LxLf+t#&poZr6W`Bo4_I^0Y~z-M1FBhOOsjiuSTt$AIiz7 z=EBNPoTXX|3w5ahHhjVv-*QP*Q6a}D-L7OK29e`pXl%$!^B@3f=&OQA<_|H_no`*j zADk^&S|U|D&MFTy92njAdOV83-cJ4Tpd4j{Co&NK=ke1=e-S#*%CFGF)8jh00v^Na zBV~cCB?0jCm^h$8r+n3kr1b(d&{|UDT{e-sF!H(7Wv|xeTf3&-JjS9epq?{KkFys^ z*t94P`o9j=8CFyZcwrfPkrH&^>As=j_Ue$GYt}1k!J1uDO%Wy>u@8o{hH+xa{N<3@ z&FPmsJm*d|-vdiWy=5AcBoZlR;&s~Xino~c*{OEIW+5;ab~$rfJ1}h!J~>X$nLZ=( zTk<#laq_0>(H@ZJIKW9{Z@rZUyfpwZ?9#ze)`>;I=z|HC$Cy1EoyU$3)*b6T^@w$^ z!PW>=NFdm^%1U4H-YE2VAgmwqY+wETBST=vu&0%sw>KXoDBC|3kqV;D^Z7WJr<{A| zhMxz1c=xk=M0_>gA2;B;N)YYh9IOABxv*4r*5t>+$46VBYl<+7FAf)E3Yp+td9m1= zf?L1dhSM~FmHNuhHxBH5xW8h0)4dov_5qy3owKv~agx%Q#KTJm>B7T68KHUdOV~)k z@bb?WQDdoP@~}froGKhtL^I{|6yOyy{hyx~DU9#V0VC>QrU{>+`j;KcF4APxoJWKS zW7Z#$iq;1%8Kqw0$mh6bM(iMX?a3k{8Yg*^XV(@QZEb?if~&L#4ljMRwb98aGiERz zr7Ok0j;mJLU0~8hOT9|)Sz!MX^?_&4fpPXGZX2O;huS`=Ge|zxjqQUKZ~Kd!UL&f} z+SUbzdN7~G_Nt$=#A`VlPq}N=ln#r*jKfQb-)8Yx%7=$PMEcy2kZf4E<`1R|#Z|uQ zxBY8Upjd3)711)t1>@*oa83`NP<`Y)$$)oBEaQAayL`JkN#U-NjYdJj<3?7gk4tzp zpKMfdw5VO#{XO8H{yAga J!m~C3{}1cA2Fd^c diff --git a/deployments/cli/community/install.sh b/deployments/cli/community/install.sh deleted file mode 100755 index 8b8a92ec122..00000000000 --- a/deployments/cli/community/install.sh +++ /dev/null @@ -1,681 +0,0 @@ -#!/bin/bash - -BRANCH=${BRANCH:-master} -SCRIPT_DIR=$PWD -SERVICE_FOLDER=plane-app -PLANE_INSTALL_DIR=$PWD/$SERVICE_FOLDER -export APP_RELEASE=stable -export DOCKERHUB_USER=artifacts.plane.so/makeplane -export PULL_POLICY=${PULL_POLICY:-if_not_present} -export GH_REPO=makeplane/plane -export RELEASE_DOWNLOAD_URL="https://github.com/$GH_REPO/releases/download" -export FALLBACK_DOWNLOAD_URL="https://raw.githubusercontent.com/$GH_REPO/$BRANCH/deployments/cli/community" - -CPU_ARCH=$(uname -m) -OS_NAME=$(uname) -UPPER_CPU_ARCH=$(tr '[:lower:]' '[:upper:]' <<< "$CPU_ARCH") - -mkdir -p $PLANE_INSTALL_DIR/archive -DOCKER_FILE_PATH=$PLANE_INSTALL_DIR/docker-compose.yaml -DOCKER_ENV_PATH=$PLANE_INSTALL_DIR/plane.env - -function print_header() { -clear - -cat <<"EOF" --------------------------------------------- - ____ _ ///////// -| _ \| | __ _ _ __ ___ ///////// -| |_) | |/ _` | '_ \ / _ \ ///// ///// -| __/| | (_| | | | | __/ ///// ///// -|_| |_|\__,_|_| |_|\___| //// - //// --------------------------------------------- -Project management tool from the future --------------------------------------------- -EOF -} - -function spinner() { - local pid=$1 - local delay=.5 - local spinstr='|/-\' - - if ! ps -p "$pid" > /dev/null; then - echo "Invalid PID: $pid" - return 1 - fi - while ps -p "$pid" > /dev/null; do - local temp=${spinstr#?} - printf " [%c] " "$spinstr" >&2 - local spinstr=$temp${spinstr%"$temp"} - sleep $delay - printf "\b\b\b\b\b\b" >&2 - done - printf " \b\b\b\b" >&2 -} - -function checkLatestRelease(){ - echo "Checking for the latest release..." >&2 - local latest_release=$(curl -s https://api.github.com/repos/$GH_REPO/releases/latest | grep -o '"tag_name": "[^"]*"' | sed 's/"tag_name": "//;s/"//g') - if [ -z "$latest_release" ]; then - echo "Failed to check for the latest release. Exiting..." >&2 - exit 1 - fi - - echo $latest_release -} - -function initialize(){ - printf "Please wait while we check the availability of Docker images for the selected release ($APP_RELEASE) with ${UPPER_CPU_ARCH} support." >&2 - - if [ "$CUSTOM_BUILD" == "true" ]; then - echo "" >&2 - echo "" >&2 - echo "${UPPER_CPU_ARCH} images are not available for selected release ($APP_RELEASE)." >&2 - echo "build" - return 1 - fi - - local IMAGE_NAME=makeplane/plane-proxy - local IMAGE_TAG=${APP_RELEASE} - docker manifest inspect "${IMAGE_NAME}:${IMAGE_TAG}" | grep -q "\"architecture\": \"${CPU_ARCH}\"" & - local pid=$! - spinner "$pid" - - echo "" >&2 - - wait "$pid" - - if [ $? -eq 0 ]; then - echo "Plane supports ${CPU_ARCH}" >&2 - echo "available" - return 0 - else - echo "" >&2 - echo "" >&2 - echo "${UPPER_CPU_ARCH} images are not available for selected release ($APP_RELEASE)." >&2 - echo "" >&2 - echo "build" - return 1 - fi -} -function getEnvValue() { - local key=$1 - local file=$2 - - if [ -z "$key" ] || [ -z "$file" ]; then - echo "Invalid arguments supplied" - exit 1 - fi - - if [ -f "$file" ]; then - grep -q "^$key=" "$file" - if [ $? -eq 0 ]; then - local value - value=$(grep "^$key=" "$file" | cut -d'=' -f2) - echo "$value" - else - echo "" - fi - fi -} -function updateEnvFile() { - local key=$1 - local value=$2 - local file=$3 - - if [ -z "$key" ] || [ -z "$value" ] || [ -z "$file" ]; then - echo "Invalid arguments supplied" - exit 1 - fi - - if [ -f "$file" ]; then - # check if key exists in the file - grep -q "^$key=" "$file" - if [ $? -ne 0 ]; then - echo "$key=$value" >> "$file" - return - else - if [ "$OS_NAME" == "Darwin" ]; then - value=$(echo "$value" | sed 's/|/\\|/g') - sed -i '' "s|^$key=.*|$key=$value|g" "$file" - else - sed -i "s/^$key=.*/$key=$value/g" "$file" - fi - fi - else - echo "File not found: $file" - exit 1 - fi -} - -function updateCustomVariables(){ - echo "Updating custom variables..." >&2 - updateEnvFile "DOCKERHUB_USER" "$DOCKERHUB_USER" "$DOCKER_ENV_PATH" - updateEnvFile "APP_RELEASE" "$APP_RELEASE" "$DOCKER_ENV_PATH" - updateEnvFile "PULL_POLICY" "$PULL_POLICY" "$DOCKER_ENV_PATH" - updateEnvFile "CUSTOM_BUILD" "$CUSTOM_BUILD" "$DOCKER_ENV_PATH" - echo "Custom variables updated successfully" >&2 -} - -function syncEnvFile(){ - echo "Syncing environment variables..." >&2 - if [ -f "$PLANE_INSTALL_DIR/plane.env.bak" ]; then - updateCustomVariables - - # READ keys of plane.env and update the values from plane.env.bak - while IFS= read -r line - do - # ignore is the line is empty or starts with # - if [ -z "$line" ] || [[ $line == \#* ]]; then - continue - fi - key=$(echo "$line" | cut -d'=' -f1) - value=$(getEnvValue "$key" "$PLANE_INSTALL_DIR/plane.env.bak") - if [ -n "$value" ]; then - updateEnvFile "$key" "$value" "$DOCKER_ENV_PATH" - fi - done < "$DOCKER_ENV_PATH" - fi - echo "Environment variables synced successfully" >&2 -} - -function buildYourOwnImage(){ - echo "Building images locally..." - - export DOCKERHUB_USER="myplane" - export APP_RELEASE="local" - export PULL_POLICY="never" - CUSTOM_BUILD="true" - - # checkout the code to ~/tmp/plane folder and build the images - local PLANE_TEMP_CODE_DIR=~/tmp/plane - rm -rf $PLANE_TEMP_CODE_DIR - mkdir -p $PLANE_TEMP_CODE_DIR - REPO=https://github.com/$GH_REPO.git - git clone "$REPO" "$PLANE_TEMP_CODE_DIR" --branch "$BRANCH" --single-branch --depth 1 - - cp "$PLANE_TEMP_CODE_DIR/deployments/cli/community/build.yml" "$PLANE_TEMP_CODE_DIR/build.yml" - - cd "$PLANE_TEMP_CODE_DIR" || exit - - /bin/bash -c "$COMPOSE_CMD -f build.yml build --no-cache" >&2 - if [ $? -ne 0 ]; then - echo "Build failed. Exiting..." - exit 1 - fi - echo "Build completed successfully" - echo "" - echo "You can now start the services by running the command: ./setup.sh start" - echo "" -} - -function install() { - echo "Begin Installing Plane" - echo "" - - if [ "$APP_RELEASE" == "stable" ]; then - export APP_RELEASE=$(checkLatestRelease) - fi - - local build_image=$(initialize) - - if [ "$build_image" == "build" ]; then - # ask for confirmation to continue building the images - echo "Do you want to continue with building the Docker images locally?" - read -p "Continue? [y/N]: " confirm - if [[ ! "$confirm" =~ ^[Yy]$ ]]; then - echo "Exiting..." - exit 0 - fi - fi - - if [ "$build_image" == "build" ]; then - download "true" - else - download "false" - fi -} - -function download() { - local LOCAL_BUILD=$1 - cd $SCRIPT_DIR - TS=$(date +%s) - if [ -f "$PLANE_INSTALL_DIR/docker-compose.yaml" ] - then - mv $PLANE_INSTALL_DIR/docker-compose.yaml $PLANE_INSTALL_DIR/archive/$TS.docker-compose.yaml - fi - - RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$RELEASE_DOWNLOAD_URL/$APP_RELEASE/docker-compose.yml?$(date +%s)") - BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') - STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') - - if [ "$STATUS" -eq 200 ]; then - echo "$BODY" > $PLANE_INSTALL_DIR/docker-compose.yaml - else - # Fallback to download from the raw github url - RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$FALLBACK_DOWNLOAD_URL/docker-compose.yml?$(date +%s)") - BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') - STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') - - if [ "$STATUS" -eq 200 ]; then - echo "$BODY" > $PLANE_INSTALL_DIR/docker-compose.yaml - else - echo "Failed to download docker-compose.yml. HTTP Status: $STATUS" - echo "URL: $RELEASE_DOWNLOAD_URL/$APP_RELEASE/docker-compose.yml" - mv $PLANE_INSTALL_DIR/archive/$TS.docker-compose.yaml $PLANE_INSTALL_DIR/docker-compose.yaml - exit 1 - fi - fi - - RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$RELEASE_DOWNLOAD_URL/$APP_RELEASE/variables.env?$(date +%s)") - BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') - STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') - - if [ "$STATUS" -eq 200 ]; then - echo "$BODY" > $PLANE_INSTALL_DIR/variables-upgrade.env - else - # Fallback to download from the raw github url - RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$FALLBACK_DOWNLOAD_URL/variables.env?$(date +%s)") - BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') - STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') - - if [ "$STATUS" -eq 200 ]; then - echo "$BODY" > $PLANE_INSTALL_DIR/variables-upgrade.env - else - echo "Failed to download variables.env. HTTP Status: $STATUS" - echo "URL: $RELEASE_DOWNLOAD_URL/$APP_RELEASE/variables.env" - mv $PLANE_INSTALL_DIR/archive/$TS.docker-compose.yaml $PLANE_INSTALL_DIR/docker-compose.yaml - exit 1 - fi - fi - - if [ -f "$DOCKER_ENV_PATH" ]; - then - cp "$DOCKER_ENV_PATH" "$PLANE_INSTALL_DIR/archive/$TS.env" - cp "$DOCKER_ENV_PATH" "$PLANE_INSTALL_DIR/plane.env.bak" - fi - - mv $PLANE_INSTALL_DIR/variables-upgrade.env $DOCKER_ENV_PATH - - syncEnvFile - - if [ "$LOCAL_BUILD" == "true" ]; then - export DOCKERHUB_USER="myplane" - export APP_RELEASE="local" - export PULL_POLICY="never" - CUSTOM_BUILD="true" - - buildYourOwnImage - - if [ $? -ne 0 ]; then - echo "" - echo "Build failed. Exiting..." - exit 1 - fi - updateCustomVariables - else - CUSTOM_BUILD="false" - updateCustomVariables - /bin/bash -c "$COMPOSE_CMD -f $DOCKER_FILE_PATH --env-file=$DOCKER_ENV_PATH pull --policy always" - - if [ $? -ne 0 ]; then - echo "" - echo "Failed to pull the images. Exiting..." - exit 1 - fi - fi - - echo "" - echo "Most recent version of Plane is now available for you to use" - echo "" - echo "In case of 'Upgrade', please check the 'plane.env 'file for any new variables and update them accordingly" - echo "" -} -function startServices() { - /bin/bash -c "$COMPOSE_CMD -f $DOCKER_FILE_PATH --env-file=$DOCKER_ENV_PATH up -d --pull if_not_present --quiet-pull" - - local migrator_container_id=$(docker container ls -aq -f "name=$SERVICE_FOLDER-migrator") - if [ -n "$migrator_container_id" ]; then - local idx=0 - while docker inspect --format='{{.State.Status}}' $migrator_container_id | grep -q "running"; do - local message=">> Waiting for Data Migration to finish" - local dots=$(printf '%*s' $idx | tr ' ' '.') - echo -ne "\r$message$dots" - ((idx++)) - sleep 1 - done - fi - printf "\r\033[K" - echo "" - echo " Data Migration completed successfully βœ…" - - # if migrator exit status is not 0, show error message and exit - if [ -n "$migrator_container_id" ]; then - local migrator_exit_code=$(docker inspect --format='{{.State.ExitCode}}' $migrator_container_id) - if [ $migrator_exit_code -ne 0 ]; then - echo "Plane Server failed to start ❌" - # stopServices - echo - echo "Please check the logs for the 'migrator' service and resolve the issue(s)." - echo "Stop the services by running the command: ./setup.sh stop" - exit 1 - fi - fi - - local api_container_id=$(docker container ls -q -f "name=$SERVICE_FOLDER-api") - local idx2=0 - while ! docker exec $api_container_id python3 -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/')" > /dev/null 2>&1; - do - local message=">> Waiting for API Service to Start" - local dots=$(printf '%*s' $idx2 | tr ' ' '.') - echo -ne "\r$message$dots" - ((idx2++)) - sleep 1 - done - printf "\r\033[K" - echo " API Service started successfully βœ…" - source "${DOCKER_ENV_PATH}" - echo " Plane Server started successfully βœ…" - echo "" - echo " You can access the application at $WEB_URL" - echo "" - -} -function stopServices() { - /bin/bash -c "$COMPOSE_CMD -f $DOCKER_FILE_PATH --env-file=$DOCKER_ENV_PATH down" -} -function restartServices() { - stopServices - startServices -} -function upgrade() { - local latest_release=$(checkLatestRelease) - - echo "" - echo "Current release: $APP_RELEASE" - - if [ "$latest_release" == "$APP_RELEASE" ]; then - echo "" - echo "You are already using the latest release" - exit 0 - fi - - echo "Latest release: $latest_release" - echo "" - - # Check for confirmation to upgrade - echo "Do you want to upgrade to the latest release ($latest_release)?" - read -p "Continue? [y/N]: " confirm - - if [[ ! "$confirm" =~ ^[Yy]$ ]]; then - echo "Exiting..." - exit 0 - fi - - export APP_RELEASE=$latest_release - - echo "Upgrading Plane to the latest release..." - echo "" - - echo "***** STOPPING SERVICES ****" - stopServices - - echo - echo "***** DOWNLOADING STABLE VERSION ****" - install - - echo "***** PLEASE VALIDATE AND START SERVICES ****" -} -function viewSpecificLogs(){ - local SERVICE_NAME=$1 - - if /bin/bash -c "$COMPOSE_CMD -f $DOCKER_FILE_PATH ps | grep -q '$SERVICE_NAME'"; then - echo "Service '$SERVICE_NAME' is running." - else - echo "Service '$SERVICE_NAME' is not running." - fi - - /bin/bash -c "$COMPOSE_CMD -f $DOCKER_FILE_PATH logs -f $SERVICE_NAME" -} -function viewLogs(){ - - ARG_SERVICE_NAME=$2 - - if [ -z "$ARG_SERVICE_NAME" ]; - then - echo - echo "Select a Service you want to view the logs for:" - echo " 1) Web" - echo " 2) Space" - echo " 3) API" - echo " 4) Worker" - echo " 5) Beat-Worker" - echo " 6) Migrator" - echo " 7) Proxy" - echo " 8) Redis" - echo " 9) Postgres" - echo " 10) Minio" - echo " 11) RabbitMQ" - echo " 0) Back to Main Menu" - echo - read -p "Service: " DOCKER_SERVICE_NAME - - until (( DOCKER_SERVICE_NAME >= 0 && DOCKER_SERVICE_NAME <= 11 )); do - echo "Invalid selection. Please enter a number between 0 and 11." - read -p "Service: " DOCKER_SERVICE_NAME - done - - if [ -z "$DOCKER_SERVICE_NAME" ]; - then - echo "INVALID SERVICE NAME SUPPLIED" - else - case $DOCKER_SERVICE_NAME in - 1) viewSpecificLogs "web";; - 2) viewSpecificLogs "space";; - 3) viewSpecificLogs "api";; - 4) viewSpecificLogs "worker";; - 5) viewSpecificLogs "beat-worker";; - 6) viewSpecificLogs "migrator";; - 7) viewSpecificLogs "proxy";; - 8) viewSpecificLogs "plane-redis";; - 9) viewSpecificLogs "plane-db";; - 10) viewSpecificLogs "plane-minio";; - 11) viewSpecificLogs "plane-mq";; - 0) askForAction;; - *) echo "INVALID SERVICE NAME SUPPLIED";; - esac - fi - elif [ -n "$ARG_SERVICE_NAME" ]; - then - ARG_SERVICE_NAME=$(echo "$ARG_SERVICE_NAME" | tr '[:upper:]' '[:lower:]') - case $ARG_SERVICE_NAME in - web) viewSpecificLogs "web";; - space) viewSpecificLogs "space";; - api) viewSpecificLogs "api";; - worker) viewSpecificLogs "worker";; - beat-worker) viewSpecificLogs "beat-worker";; - migrator) viewSpecificLogs "migrator";; - proxy) viewSpecificLogs "proxy";; - redis) viewSpecificLogs "plane-redis";; - postgres) viewSpecificLogs "plane-db";; - minio) viewSpecificLogs "plane-minio";; - rabbitmq) viewSpecificLogs "plane-mq";; - *) echo "INVALID SERVICE NAME SUPPLIED";; - esac - else - echo "INVALID SERVICE NAME SUPPLIED" - fi -} -function backup_container_dir() { - local BACKUP_FOLDER=$1 - local CONTAINER_NAME=$2 - local CONTAINER_DATA_DIR=$3 - local SERVICE_FOLDER=$4 - - echo "Backing up $CONTAINER_NAME data..." - local CONTAINER_ID=$(/bin/bash -c "$COMPOSE_CMD -f $DOCKER_FILE_PATH ps -q $CONTAINER_NAME") - if [ -z "$CONTAINER_ID" ]; then - echo "Error: $CONTAINER_NAME container not found. Make sure the services are running." - return 1 - fi - - # Create a temporary directory for the backup - mkdir -p "$BACKUP_FOLDER/$SERVICE_FOLDER" - - # Copy the data directory from the running container - echo "Copying $CONTAINER_NAME data directory..." - docker cp -q "$CONTAINER_ID:$CONTAINER_DATA_DIR/." "$BACKUP_FOLDER/$SERVICE_FOLDER/" - local cp_status=$? - - if [ $cp_status -ne 0 ]; then - echo "Error: Failed to copy $SERVICE_FOLDER data" - rm -rf $BACKUP_FOLDER/$SERVICE_FOLDER - return 1 - fi - - # Create tar.gz of the data - cd "$BACKUP_FOLDER" - tar -czf "${SERVICE_FOLDER}.tar.gz" "$SERVICE_FOLDER/" - local tar_status=$? - if [ $tar_status -eq 0 ]; then - rm -rf "$SERVICE_FOLDER/" - fi - cd - > /dev/null - - if [ $tar_status -ne 0 ]; then - echo "Error: Failed to create tar archive" - return 1 - fi - - echo "Successfully backed up $SERVICE_FOLDER data" -} - -function backupData() { - local datetime=$(date +"%Y%m%d-%H%M") - local BACKUP_FOLDER=$PLANE_INSTALL_DIR/backup/$datetime - mkdir -p "$BACKUP_FOLDER" - - # Check if docker-compose.yml exists - if [ ! -f "$DOCKER_FILE_PATH" ]; then - echo "Error: docker-compose.yml not found at $DOCKER_FILE_PATH" - exit 1 - fi - - backup_container_dir "$BACKUP_FOLDER" "plane-db" "/var/lib/postgresql/data" "pgdata" || exit 1 - backup_container_dir "$BACKUP_FOLDER" "plane-minio" "/export" "uploads" || exit 1 - backup_container_dir "$BACKUP_FOLDER" "plane-mq" "/var/lib/rabbitmq" "rabbitmq_data" || exit 1 - backup_container_dir "$BACKUP_FOLDER" "plane-redis" "/data" "redisdata" || exit 1 - - echo "" - echo "Backup completed successfully. Backup files are stored in $BACKUP_FOLDER" - echo "" -} -function askForAction() { - local DEFAULT_ACTION=$1 - - if [ -z "$DEFAULT_ACTION" ]; - then - echo - echo "Select a Action you want to perform:" - echo " 1) Install" - echo " 2) Start" - echo " 3) Stop" - echo " 4) Restart" - echo " 5) Upgrade" - echo " 6) View Logs" - echo " 7) Backup Data" - echo " 8) Exit" - echo - read -p "Action [2]: " ACTION - until [[ -z "$ACTION" || "$ACTION" =~ ^[1-8]$ ]]; do - echo "$ACTION: invalid selection." - read -p "Action [2]: " ACTION - done - - if [ -z "$ACTION" ]; - then - ACTION=2 - fi - echo - fi - - if [ "$ACTION" == "1" ] || [ "$DEFAULT_ACTION" == "install" ]; - then - install - # askForAction - elif [ "$ACTION" == "2" ] || [ "$DEFAULT_ACTION" == "start" ]; - then - startServices - # askForAction - elif [ "$ACTION" == "3" ] || [ "$DEFAULT_ACTION" == "stop" ]; - then - stopServices - # askForAction - elif [ "$ACTION" == "4" ] || [ "$DEFAULT_ACTION" == "restart" ]; - then - restartServices - # askForAction - elif [ "$ACTION" == "5" ] || [ "$DEFAULT_ACTION" == "upgrade" ]; - then - upgrade - # askForAction - elif [ "$ACTION" == "6" ] || [ "$DEFAULT_ACTION" == "logs" ]; - then - viewLogs "$@" - askForAction - elif [ "$ACTION" == "7" ] || [ "$DEFAULT_ACTION" == "backup" ]; - then - backupData - elif [ "$ACTION" == "8" ] - then - exit 0 - else - echo "INVALID ACTION SUPPLIED" - fi -} - -# if docker-compose is installed -if command -v docker-compose &> /dev/null -then - COMPOSE_CMD="docker-compose" -else - COMPOSE_CMD="docker compose" -fi - -if [ "$CPU_ARCH" == "x86_64" ] || [ "$CPU_ARCH" == "amd64" ]; then - CPU_ARCH="amd64" -elif [ "$CPU_ARCH" == "aarch64" ] || [ "$CPU_ARCH" == "arm64" ]; then - CPU_ARCH="arm64" -fi - -if [ -f "$DOCKER_ENV_PATH" ]; then - DOCKERHUB_USER=$(getEnvValue "DOCKERHUB_USER" "$DOCKER_ENV_PATH") - APP_RELEASE=$(getEnvValue "APP_RELEASE" "$DOCKER_ENV_PATH") - PULL_POLICY=$(getEnvValue "PULL_POLICY" "$DOCKER_ENV_PATH") - CUSTOM_BUILD=$(getEnvValue "CUSTOM_BUILD" "$DOCKER_ENV_PATH") - - if [ -z "$DOCKERHUB_USER" ]; then - DOCKERHUB_USER=artifacts.plane.so/makeplane - updateEnvFile "DOCKERHUB_USER" "$DOCKERHUB_USER" "$DOCKER_ENV_PATH" - fi - - if [ -z "$APP_RELEASE" ]; then - APP_RELEASE=stable - updateEnvFile "APP_RELEASE" "$APP_RELEASE" "$DOCKER_ENV_PATH" - fi - - if [ -z "$PULL_POLICY" ]; then - PULL_POLICY=if_not_present - updateEnvFile "PULL_POLICY" "$PULL_POLICY" "$DOCKER_ENV_PATH" - fi - - if [ -z "$CUSTOM_BUILD" ]; then - CUSTOM_BUILD=false - updateEnvFile "CUSTOM_BUILD" "$CUSTOM_BUILD" "$DOCKER_ENV_PATH" - fi -fi - -print_header -askForAction "$@" diff --git a/deployments/cli/community/migration-0.13-0.14.sh b/deployments/cli/community/migration-0.13-0.14.sh deleted file mode 100755 index d03f877805c..00000000000 --- a/deployments/cli/community/migration-0.13-0.14.sh +++ /dev/null @@ -1,118 +0,0 @@ -#!/bin/bash - -echo ' -****************************************************************** - -This script is solely for the migration purpose only. -This is a 1 time migration of volume data from v0.13.2 => v0.14.x - -Assumption: -1. Postgres data volume name ends with _pgdata -2. Minio data volume name ends with _uploads -3. Redis data volume name ends with _redisdata - -Any changes to this script can break the migration. - -Before you proceed, make sure you run the below command -to know the docker volumes - -docker volume ls -q | grep -i "_pgdata" -docker volume ls -q | grep -i "_uploads" -docker volume ls -q | grep -i "_redisdata" - -******************************************************* -' - -DOWNLOAD_FOL=./download -rm -rf ${DOWNLOAD_FOL} -mkdir -p ${DOWNLOAD_FOL} - -function volumeExists { - if [ "$(docker volume ls -f name=$1 | awk '{print $NF}' | grep -E '^'$1'$')" ]; then - return 0 - else - return 1 - fi -} - -function readPrefixes(){ - echo '' - echo 'Given below list of REDIS volumes, identify the prefix of source and destination volumes leaving "_redisdata" ' - echo '---------------------' - docker volume ls -q | grep -i "_redisdata" - echo '' - - read -p "Provide the Source Volume Prefix : " SRC_VOL_PREFIX - until [ "$SRC_VOL_PREFIX" ]; do - read -p "Provide the Source Volume Prefix : " SRC_VOL_PREFIX - done - - read -p "Provide the Destination Volume Prefix : " DEST_VOL_PREFIX - until [ "$DEST_VOL_PREFIX" ]; do - read -p "Provide the Source Volume Prefix : " DEST_VOL_PREFIX - done - - echo '' - echo 'Prefix Provided ' - echo " Source : ${SRC_VOL_PREFIX}" - echo " Destination : ${DEST_VOL_PREFIX}" - echo '---------------------------------------' -} - -function migrate(){ - - SRC_VOLUME=${SRC_VOL_PREFIX}_${VOL_NAME_SUFFIX} - DEST_VOLUME=${DEST_VOL_PREFIX}_${VOL_NAME_SUFFIX} - - if volumeExists $SRC_VOLUME; then - if volumeExists $DEST_VOLUME; then - GOOD_TO_GO=1 - else - echo "Destination Volume '$DEST_VOLUME' does not exist" - echo '' - fi - else - echo "Source Volume '$SRC_VOLUME' does not exist" - echo '' - fi - - if [ $GOOD_TO_GO = 1 ]; then - - echo "MIGRATING ${VOL_NAME_SUFFIX} FROM ${SRC_VOLUME} => ${DEST_VOLUME}" - - TEMP_CONTAINER=$(docker run -d -v $SRC_VOLUME:$CONTAINER_VOL_FOLDER busybox true) - docker cp -q $TEMP_CONTAINER:$CONTAINER_VOL_FOLDER ${DOWNLOAD_FOL}/${VOL_NAME_SUFFIX} - docker rm $TEMP_CONTAINER &> /dev/null - - TEMP_CONTAINER=$(docker run -d -v $DEST_VOLUME:$CONTAINER_VOL_FOLDER busybox true) - if [ "$VOL_NAME_SUFFIX" = "pgdata" ]; then - docker cp -q ${DOWNLOAD_FOL}/${VOL_NAME_SUFFIX} $TEMP_CONTAINER:$CONTAINER_VOL_FOLDER/_temp - docker run --rm -v $DEST_VOLUME:$CONTAINER_VOL_FOLDER \ - -e DATA_FOLDER="${CONTAINER_VOL_FOLDER}" \ - busybox /bin/sh -c 'cp -Rf $DATA_FOLDER/_temp/* $DATA_FOLDER ' - else - docker cp -q ${DOWNLOAD_FOL}/${VOL_NAME_SUFFIX} $TEMP_CONTAINER:$CONTAINER_VOL_FOLDER - fi - docker rm $TEMP_CONTAINER &> /dev/null - - echo '' - fi -} - -readPrefixes - -# MIGRATE DB -CONTAINER_VOL_FOLDER=/var/lib/postgresql/data -VOL_NAME_SUFFIX=pgdata -migrate - -# MIGRATE REDIS -CONTAINER_VOL_FOLDER=/data -VOL_NAME_SUFFIX=redisdata -migrate - -# MIGRATE MINIO -CONTAINER_VOL_FOLDER=/export -VOL_NAME_SUFFIX=uploads -migrate - diff --git a/deployments/cli/community/restore-airgapped.sh b/deployments/cli/community/restore-airgapped.sh deleted file mode 100755 index 9da02fd869d..00000000000 --- a/deployments/cli/community/restore-airgapped.sh +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/bash -+set -euo pipefail - -function print_header() { -clear - -cat <<"EOF" --------------------------------------------- - ____ _ ///////// -| _ \| | __ _ _ __ ___ ///////// -| |_) | |/ _` | '_ \ / _ \ ///// ///// -| __/| | (_| | | | | __/ ///// ///// -|_| |_|\__,_|_| |_|\___| //// - //// --------------------------------------------- -Project management tool from the future --------------------------------------------- -EOF -} - -function restoreData() { - - echo "" - echo "****************************************************" - echo "We are about to restore your data from the backup files." - echo "****************************************************" - echo "" - - # set the backup folder path - BACKUP_FOLDER=${1} - - if [ -z "$BACKUP_FOLDER" ]; then - BACKUP_FOLDER="$PWD/backup" - read -p "Enter the backup folder path [$BACKUP_FOLDER]: " BACKUP_FOLDER - if [ -z "$BACKUP_FOLDER" ]; then - BACKUP_FOLDER="$PWD/backup" - fi - fi - - # check if the backup folder exists - if [ ! -d "$BACKUP_FOLDER" ]; then - echo "Error: Backup folder not found at $BACKUP_FOLDER" - exit 1 - fi - - # check if there are any .tar.gz files in the backup folder - if ! ls "$BACKUP_FOLDER"/*.tar.gz 1> /dev/null 2>&1; then - echo "Error: Backup folder does not contain .tar.gz files" - exit 1 - fi - - echo "" - echo "Using backup folder: $BACKUP_FOLDER" - echo "" - - # ask for current install path - AIRGAPPED_INSTALL_PATH="$HOME/planeairgapped" - read -p "Enter the airgapped instance install path [$AIRGAPPED_INSTALL_PATH]: " AIRGAPPED_INSTALL_PATH - if [ -z "$AIRGAPPED_INSTALL_PATH" ]; then - AIRGAPPED_INSTALL_PATH="$HOME/planeairgapped" - fi - - # check if the airgapped instance install path exists - if [ ! -d "$AIRGAPPED_INSTALL_PATH" ]; then - echo "Error: Airgapped instance install path not found at $AIRGAPPED_INSTALL_PATH" - exit 1 - fi - - echo "" - echo "Using airgapped instance install path: $AIRGAPPED_INSTALL_PATH" - echo "" - - # check if the docker-compose.yaml exists - if [ ! -f "$AIRGAPPED_INSTALL_PATH/docker-compose.yml" ]; then - echo "Error: docker-compose.yml not found at $AIRGAPPED_INSTALL_PATH/docker-compose.yml" - exit 1 - fi - - local dockerServiceStatus - if command -v jq &> /dev/null; then - dockerServiceStatus=$($COMPOSE_CMD ls --filter name=plane-airgapped --format=json | jq -r .[0].Status) - else - dockerServiceStatus=$($COMPOSE_CMD ls --filter name=plane-airgapped | grep -o "running" | head -n 1) - fi - - if [[ $dockerServiceStatus == "running" ]]; then - echo "Plane Airgapped is running. Please STOP the Plane Airgapped before restoring data." - exit 1 - fi - - CURRENT_USER_ID=$(id -u) - CURRENT_GROUP_ID=$(id -g) - - # if the data folder not exists, create it - if [ ! -d "$AIRGAPPED_INSTALL_PATH/data" ]; then - mkdir -p "$AIRGAPPED_INSTALL_PATH/data" - chown -R $CURRENT_USER_ID:$CURRENT_GROUP_ID "$AIRGAPPED_INSTALL_PATH/data" - fi - - for BACKUP_FILE in "$BACKUP_FOLDER/*.tar.gz"; do - if [ -e "$BACKUP_FILE" ]; then - - # get the basefilename without the extension - BASE_FILE_NAME=$(basename "$BACKUP_FILE" ".tar.gz") - - # extract the restoreFile to the airgapped instance install path - echo "Restoring $BASE_FILE_NAME" - rm -rf "$AIRGAPPED_INSTALL_PATH/data/$BASE_FILE_NAME" || true - - tar -xvzf "$BACKUP_FILE" -C "$AIRGAPPED_INSTALL_PATH/data/" - if [ $? -ne 0 ]; then - echo "Error: Failed to extract $BACKUP_FILE" - exit 1 - fi - chown -R $CURRENT_USER_ID:$CURRENT_GROUP_ID "$AIRGAPPED_INSTALL_PATH/data/$BASE_FILE_NAME" - if [ $? -ne 0 ]; then - echo "Error: Failed to change ownership of $AIRGAPPED_INSTALL_PATH/data/$BASE_FILE_NAME" - exit 1 - fi - else - echo "No .tar.gz files found in the current directory." - echo "" - echo "Please provide the path to the backup file." - echo "" - echo "Usage: $0 /path/to/backup" - exit 1 - fi - done - - echo "" - echo "Restore completed successfully." - echo "" -} - -# if docker-compose is installed -if command -v docker-compose &> /dev/null -then - COMPOSE_CMD="docker-compose" -else - COMPOSE_CMD="docker compose" -fi - -print_header -restoreData "$@" diff --git a/deployments/cli/community/restore.sh b/deployments/cli/community/restore.sh deleted file mode 100755 index 23b8de6cfd3..00000000000 --- a/deployments/cli/community/restore.sh +++ /dev/null @@ -1,123 +0,0 @@ -#!/bin/bash - -function print_header() { -clear - -cat <<"EOF" --------------------------------------------- - ____ _ ///////// -| _ \| | __ _ _ __ ___ ///////// -| |_) | |/ _` | '_ \ / _ \ ///// ///// -| __/| | (_| | | | | __/ ///// ///// -|_| |_|\__,_|_| |_|\___| //// - //// --------------------------------------------- -Project management tool from the future --------------------------------------------- -EOF -} - -function restoreSingleVolume() { - selectedVolume=$1 - backupFolder=$2 - restoreFile=$3 - - docker volume rm "$selectedVolume" > /dev/null 2>&1 - - if [ $? -ne 0 ]; then - echo "Error: Failed to remove volume $selectedVolume" - echo "" - return 1 - fi - - docker volume create "$selectedVolume" > /dev/null 2>&1 - if [ $? -ne 0 ]; then - echo "Error: Failed to create volume $selectedVolume" - echo "" - return 1 - fi - - docker run --rm \ - -e TAR_NAME="$restoreFile" \ - -v "$selectedVolume":"/vol" \ - -v "$backupFolder":/backup \ - busybox sh -c 'mkdir -p /restore && tar -xzf "/backup/${TAR_NAME}.tar.gz" -C /restore && mv /restore/${TAR_NAME}/* /vol' - - if [ $? -ne 0 ]; then - echo "Error: Failed to restore volume ${selectedVolume} from ${restoreFile}.tar.gz" - echo "" - return 1 - fi - echo ".....Successfully restored volume $selectedVolume from ${restoreFile}.tar.gz" - echo "" -} - -function restoreData() { - print_header - local BACKUP_FOLDER=${1:-$PWD} - - local dockerServiceStatus - dockerServiceStatus=$($COMPOSE_CMD ls --filter name=plane-app --format=json | jq -r .[0].Status) - local dockerServicePrefix - dockerServicePrefix="running" - - if [[ $dockerServiceStatus == $dockerServicePrefix* ]]; then - echo "Plane App is running. Please STOP the Plane App before restoring data." - exit 1 - fi - - local volume_suffix - volume_suffix="_pgdata|_redisdata|_uploads|_rabbitmq_data" - local volumes - volumes=$(docker volume ls -f "name=plane-app" --format "{{.Name}}" | grep -E "$volume_suffix") - # Check if there are any matching volumes - if [ -z "$volumes" ]; then - echo ".....No volumes found starting with 'plane-app'" - exit 1 - fi - - - for BACKUP_FILE in $BACKUP_FOLDER/*.tar.gz; do - if [ -e "$BACKUP_FILE" ]; then - - local restoreFileName - restoreFileName=$(basename "$BACKUP_FILE") - restoreFileName="${restoreFileName%.tar.gz}" - - local restoreVolName - restoreVolName="plane-app_${restoreFileName}" - echo "Found $BACKUP_FILE" - - local docVol - docVol=$(docker volume ls -f "name=$restoreVolName" --format "{{.Name}}" | grep -E "$volume_suffix") - - if [ -z "$docVol" ]; then - echo "Skipping: No volume found with name $restoreVolName" - else - echo ".....Restoring $docVol" - restoreSingleVolume "$docVol" "$BACKUP_FOLDER" "$restoreFileName" - fi - else - echo "No .tar.gz files found in the current directory." - echo "" - echo "Please provide the path to the backup file." - echo "" - echo "Usage: ./restore.sh /path/to/backup" - exit 1 - fi - done - - echo "" - echo "Restore completed successfully." - echo "" -} - -# if docker-compose is installed -if command -v docker-compose &> /dev/null -then - COMPOSE_CMD="docker-compose" -else - COMPOSE_CMD="docker compose" -fi - -restoreData "$@" \ No newline at end of file diff --git a/deployments/cli/community/variables.env b/deployments/cli/community/variables.env deleted file mode 100644 index 66265f9a1e8..00000000000 --- a/deployments/cli/community/variables.env +++ /dev/null @@ -1,79 +0,0 @@ -APP_DOMAIN=localhost -APP_RELEASE=stable -SSL=false - -WEB_REPLICAS=1 -SPACE_REPLICAS=1 -ADMIN_REPLICAS=1 -API_REPLICAS=1 -WORKER_REPLICAS=1 -BEAT_WORKER_REPLICAS=1 -LIVE_REPLICAS=1 - -LISTEN_HTTP_PORT=80 -LISTEN_HTTPS_PORT=443 - -WEB_URL=http://${APP_DOMAIN} -DEBUG=0 -CORS_ALLOWED_ORIGINS=http://${APP_DOMAIN} -API_BASE_URL=http://api:8000 - -#DB SETTINGS -PGHOST=plane-db -PGDATABASE=plane -POSTGRES_USER=plane -POSTGRES_PASSWORD=plane -POSTGRES_DB=plane -POSTGRES_PORT=5432 -PGDATA=/var/lib/postgresql/data -DATABASE_URL= - -# REDIS SETTINGS -REDIS_HOST=plane-redis -REDIS_PORT=6379 -REDIS_URL= - -# RabbitMQ Settings -RABBITMQ_HOST=plane-mq -RABBITMQ_PORT=5672 -RABBITMQ_USER=plane -RABBITMQ_PASSWORD=plane -RABBITMQ_VHOST=plane -AMQP_URL= - -# If SSL Cert to be generated, set CERT_EMAIl="email " -CERT_ACME_CA=https://acme-v02.api.letsencrypt.org/directory -TRUSTED_PROXIES=0.0.0.0/0 -SITE_ADDRESS=:80 -CERT_EMAIL= - - - -# For DNS Challenge based certificate generation, set the CERT_ACME_DNS, CERT_EMAIL -# CERT_ACME_DNS="acme_dns " -CERT_ACME_DNS= - - -# Secret Key -SECRET_KEY=60gp0byfz2dvffa45cxl20p1scy9xbpf6d8c5y0geejgkyp1b5 - -# DATA STORE SETTINGS -USE_MINIO=1 -AWS_REGION= -AWS_ACCESS_KEY_ID=access-key -AWS_SECRET_ACCESS_KEY=secret-key -AWS_S3_ENDPOINT_URL=http://plane-minio:9000 -AWS_S3_BUCKET_NAME=uploads -FILE_SIZE_LIMIT=5242880 - -# Gunicorn Workers -GUNICORN_WORKERS=1 - -# UNCOMMENT `DOCKER_PLATFORM` IF YOU ARE ON `ARM64` AND DOCKER IMAGE IS NOT AVAILABLE FOR RESPECTIVE `APP_RELEASE` -# DOCKER_PLATFORM=linux/amd64 - -# Force HTTPS for handling SSL Termination -MINIO_ENDPOINT_SSL=0 - -# API key rate limit -API_KEY_RATE_LIMIT=60/minute diff --git a/deployments/kubernetes/community/README.md b/deployments/kubernetes/community/README.md deleted file mode 100644 index c1000c7327c..00000000000 --- a/deployments/kubernetes/community/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Helm Chart: Plane Community - -Click on the below link to access the helm chart instructions. - -[![Artifact Hub](https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/makeplane)](https://artifacthub.io/packages/helm/makeplane/plane-ce) diff --git a/deployments/swarm/community/swarm.sh b/deployments/swarm/community/swarm.sh deleted file mode 100755 index d496c25e6de..00000000000 --- a/deployments/swarm/community/swarm.sh +++ /dev/null @@ -1,611 +0,0 @@ -#!/bin/bash - -BRANCH=${BRANCH:-master} -SERVICE_FOLDER=plane-app -SCRIPT_DIR=$PWD -PLANE_INSTALL_DIR=$PWD/$SERVICE_FOLDER -export APP_RELEASE="stable" -export DOCKERHUB_USER=artifacts.plane.so/makeplane - -export GH_REPO=makeplane/plane -export RELEASE_DOWNLOAD_URL="https://github.com/$GH_REPO/releases/download" -export FALLBACK_DOWNLOAD_URL="https://raw.githubusercontent.com/$GH_REPO/$BRANCH/deployments/cli/community" - -OS_NAME=$(uname) - -# Create necessary directories -mkdir -p $PLANE_INSTALL_DIR/archive - -DOCKER_FILE_PATH=$PLANE_INSTALL_DIR/docker-compose.yml -DOCKER_ENV_PATH=$PLANE_INSTALL_DIR/plane.env - -function print_header() { -clear - -cat <<"EOF" --------------------------------------------- - ____ _ ///////// -| _ \| | __ _ _ __ ___ ///////// -| |_) | |/ _` | '_ \ / _ \ ///// ///// -| __/| | (_| | | | | __/ ///// ///// -|_| |_|\__,_|_| |_|\___| //// - //// --------------------------------------------- -Project management tool from the future --------------------------------------------- -EOF -} - -function checkLatestRelease(){ - echo "Checking for the latest release..." >&2 - local latest_release=$(curl -s https://api.github.com/repos/$GH_REPO/releases/latest | grep -o '"tag_name": "[^"]*"' | sed 's/"tag_name": "//;s/"//g') - if [ -z "$latest_release" ]; then - echo "Failed to check for the latest release. Exiting..." >&2 - exit 1 - fi - - echo $latest_release -} - -# Function to read stack name from env file -function readStackName() { - if [ -f "$DOCKER_ENV_PATH" ]; then - local saved_stack_name=$(grep "^STACK_NAME=" "$DOCKER_ENV_PATH" | cut -d'=' -f2) - if [ -n "$saved_stack_name" ]; then - stack_name=$saved_stack_name - return 1 - fi - fi - return 0 -} - -# Function to get stack name (either from env or user input) -function getStackName() { - read -p "Enter stack name [plane]: " input_stack_name - if [ -z "$input_stack_name" ]; then - input_stack_name="plane" - fi - stack_name=$input_stack_name - updateEnvFile "STACK_NAME" "$stack_name" "$DOCKER_ENV_PATH" - echo "Using stack name: $stack_name" -} - -function syncEnvFile(){ - echo "Syncing environment variables..." >&2 - if [ -f "$PLANE_INSTALL_DIR/plane.env.bak" ]; then - # READ keys of plane.env and update the values from plane.env.bak - while IFS= read -r line - do - # ignore if the line is empty or starts with # - if [ -z "$line" ] || [[ $line == \#* ]]; then - continue - fi - key=$(echo "$line" | cut -d'=' -f1) - value=$(getEnvValue "$key" "$PLANE_INSTALL_DIR/plane.env.bak") - if [ -n "$value" ]; then - updateEnvFile "$key" "$value" "$DOCKER_ENV_PATH" - fi - done < "$DOCKER_ENV_PATH" - - value=$(getEnvValue "STACK_NAME" "$PLANE_INSTALL_DIR/plane.env.bak") - if [ -n "$value" ]; then - updateEnvFile "STACK_NAME" "$value" "$DOCKER_ENV_PATH" - fi - fi - echo "Environment variables synced successfully" >&2 - rm -f $PLANE_INSTALL_DIR/plane.env.bak -} - -function getEnvValue() { - local key=$1 - local file=$2 - - if [ -z "$key" ] || [ -z "$file" ]; then - echo "Invalid arguments supplied" - exit 1 - fi - - if [ -f "$file" ]; then - grep -q "^$key=" "$file" - if [ $? -eq 0 ]; then - local value - value=$(grep "^$key=" "$file" | cut -d'=' -f2) - echo "$value" - else - echo "" - fi - fi -} - -function updateEnvFile() { - local key=$1 - local value=$2 - local file=$3 - - if [ -z "$key" ] || [ -z "$value" ] || [ -z "$file" ]; then - echo "Invalid arguments supplied" - exit 1 - fi - - if [ -f "$file" ]; then - # check if key exists in the file - grep -q "^$key=" "$file" - if [ $? -ne 0 ]; then - echo "$key=$value" >> "$file" - return - else - if [ "$OS_NAME" == "Darwin" ]; then - value=$(echo "$value" | sed 's/|/\\|/g') - sed -i '' "s|^$key=.*|$key=$value|g" "$file" - else - sed -i "s/^$key=.*/$key=$value/g" "$file" - fi - fi - else - echo "File not found: $file" - exit 1 - fi -} - -function download() { - cd $SCRIPT_DIR || exit 1 - TS=$(date +%s) - if [ -f "$PLANE_INSTALL_DIR/docker-compose.yml" ]; then - mv $PLANE_INSTALL_DIR/docker-compose.yml $PLANE_INSTALL_DIR/archive/$TS.docker-compose.yml - fi - - echo $RELEASE_DOWNLOAD_URL - echo $FALLBACK_DOWNLOAD_URL - echo $APP_RELEASE - - RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$RELEASE_DOWNLOAD_URL/$APP_RELEASE/docker-compose.yml?$(date +%s)") - BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') - STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') - - if [ "$STATUS" -eq 200 ]; then - echo "$BODY" > $PLANE_INSTALL_DIR/docker-compose.yml - else - # Fallback to download from the raw github url - RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$FALLBACK_DOWNLOAD_URL/docker-compose.yml?$(date +%s)") - BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') - STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') - - if [ "$STATUS" -eq 200 ]; then - echo "$BODY" > $PLANE_INSTALL_DIR/docker-compose.yml - else - echo "Failed to download docker-compose.yml. HTTP Status: $STATUS" - echo "URL: $RELEASE_DOWNLOAD_URL/$APP_RELEASE/docker-compose.yml" - mv $PLANE_INSTALL_DIR/archive/$TS.docker-compose.yml $PLANE_INSTALL_DIR/docker-compose.yml - exit 1 - fi - fi - - RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$RELEASE_DOWNLOAD_URL/$APP_RELEASE/variables.env?$(date +%s)") - BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') - STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') - - if [ "$STATUS" -eq 200 ]; then - echo "$BODY" > $PLANE_INSTALL_DIR/variables-upgrade.env - else - # Fallback to download from the raw github url - RESPONSE=$(curl -H 'Cache-Control: no-cache, no-store' -s -w "HTTPSTATUS:%{http_code}" "$FALLBACK_DOWNLOAD_URL/variables.env?$(date +%s)") - BODY=$(echo "$RESPONSE" | sed -e 's/HTTPSTATUS\:.*//g') - STATUS=$(echo "$RESPONSE" | tr -d '\n' | sed -e 's/.*HTTPSTATUS://') - - if [ "$STATUS" -eq 200 ]; then - echo "$BODY" > $PLANE_INSTALL_DIR/variables-upgrade.env - else - echo "Failed to download variables.env. HTTP Status: $STATUS" - echo "URL: $RELEASE_DOWNLOAD_URL/$APP_RELEASE/variables.env" - mv $PLANE_INSTALL_DIR/archive/$TS.docker-compose.yml $PLANE_INSTALL_DIR/docker-compose.yml - exit 1 - fi - fi - - if [ -f "$DOCKER_ENV_PATH" ]; - then - cp "$DOCKER_ENV_PATH" "$PLANE_INSTALL_DIR/archive/$TS.env" - cp "$DOCKER_ENV_PATH" "$PLANE_INSTALL_DIR/plane.env.bak" - fi - - mv $PLANE_INSTALL_DIR/variables-upgrade.env $DOCKER_ENV_PATH - - syncEnvFile - - updateEnvFile "APP_RELEASE" "$APP_RELEASE" "$DOCKER_ENV_PATH" - -} -function deployStack() { - # Check if docker compose file and env file exist - if [ ! -f "$DOCKER_FILE_PATH" ] || [ ! -f "$DOCKER_ENV_PATH" ]; then - echo "Configuration files not found" - echo "Downloading it now......" - APP_RELEASE=$(checkLatestRelease) - download - fi - if [ -z "$stack_name" ]; then - getStackName - fi - echo "Starting ${stack_name} stack..." - - # Pull envs - if [ -f "$DOCKER_ENV_PATH" ]; then - set -o allexport; source $DOCKER_ENV_PATH; set +o allexport; - else - echo "Environment file not found: $DOCKER_ENV_PATH" - exit 1 - fi - - # Deploy the stack - docker stack deploy -c $DOCKER_FILE_PATH $stack_name - - echo "Waiting for services to be deployed..." - sleep 10 - - # Check migrator service - local migrator_service=$(docker service ls --filter name=${stack_name}_migrator -q) - if [ -n "$migrator_service" ]; then - echo ">> Waiting for Data Migration to finish" - while docker service ls --filter name=${stack_name}_migrator | grep -q "running"; do - echo -n "." - sleep 1 - done - echo "" - - # Get the most recent container for the migrator service - local migrator_container=$(docker ps -a --filter name=${stack_name}_migrator --latest -q) - - if [ -n "$migrator_container" ]; then - # Get the exit code of the container - local exit_code=$(docker inspect --format='{{.State.ExitCode}}' $migrator_container) - - if [ "$exit_code" != "0" ]; then - echo "Server failed to start ❌" - echo "Migration failed with exit code: $exit_code" - echo "Please check the logs for the 'migrator' service and resolve the issue(s)." - echo "Stop the services by running the command: ./swarm.sh stop" - exit 1 - else - echo " Data Migration completed successfully βœ…" - fi - else - echo "Warning: Could not find migrator container to check exit status" - fi - fi - - # Check API service - local api_service=$(docker service ls --filter name=${stack_name}_api -q) - while docker service ls --filter name=${stack_name}_api | grep -q "running"; do - local running_container=$(docker ps --filter "name=${stack_name}_api" --filter "status=running" -q) - if [ -n "$running_container" ]; then - if docker container logs $running_container 2>/dev/null | grep -q "Application Startup Complete"; then - break - fi - fi - sleep 2 - done - - if [ -z "$api_service" ]; then - echo "Plane Server failed to start ❌" - echo "Please check the logs for the 'api' service and resolve the issue(s)." - echo "Stop the services by running the command: ./swarm.sh stop" - exit 1 - fi - echo " Plane Server started successfully βœ…" - echo "" - echo " You can access the application at $WEB_URL" - echo "" -} - -function removeStack() { - if [ -z "$stack_name" ]; then - echo "Stack name not found" - exit 1 - fi - echo "Removing ${stack_name} stack..." - docker stack rm "$stack_name" - echo "Waiting for services to be removed..." - while docker stack ls | grep -q "$stack_name"; do - sleep 1 - done - sleep 20 - echo "Services stopped successfully βœ…" -} - -function viewStatus() { - echo "Checking status of ${stack_name} stack..." - if [ -z "$stack_name" ]; then - echo "Stack name not found" - exit 1 - fi - docker stack ps "$stack_name" -} - -function redeployStack() { - removeStack - echo "ReDeploying ${stack_name} stack..." - deployStack -} - -function upgrade() { - - echo "Checking status of ${stack_name} stack..." - if [ -z "$stack_name" ]; then - echo "Stack name not found" - exit 1 - fi - - local latest_release=$(checkLatestRelease) - - echo "" - echo "Current release: $APP_RELEASE" - - if [ "$latest_release" == "$APP_RELEASE" ]; then - echo "" - echo "You are already using the latest release" - exit 0 - fi - - echo "Latest release: $latest_release" - echo "" - - # Check for confirmation to upgrade - echo "Do you want to upgrade to the latest release ($latest_release)?" - read -p "Continue? [y/N]: " confirm - - if [[ ! "$confirm" =~ ^[Yy]$ ]]; then - echo "Exiting..." - exit 0 - fi - - export APP_RELEASE=$latest_release - - # check if stack exists - echo "Upgrading ${stack_name} stack..." - - # check env file and take backup - if [ -f "$DOCKER_ENV_PATH" ]; then - cp "$DOCKER_ENV_PATH" "${DOCKER_ENV_PATH}.bak" - fi - - download - redeployStack -} - -function viewSpecificLogs() { - local service=$1 - - # Input validation - if [ -z "$service" ]; then - echo "Error: Please specify a service name" - return 1 - fi - - # Main loop for service logs - while true; do - # Get all running containers for the service - local running_containers=$(docker ps --filter "name=${stack_name}_${service}" --filter "status=running" -q) - - # If no running containers found, try service logs - if [ -z "$running_containers" ]; then - echo "No running containers found for ${stack_name}_${service}, checking service logs..." - if docker service inspect ${stack_name}_${service} >/dev/null 2>&1; then - echo "Press Ctrl+C or 'q' to exit logs" - docker service logs ${stack_name}_${service} -f - break - else - echo "Error: No running containers or services found for ${stack_name}_${service}" - return 1 - fi - return - fi - - # If multiple containers are running, let user choose - if [ $(echo "$running_containers" | grep -v '^$' | wc -l) -gt 1 ]; then - clear - echo "Multiple containers found for ${stack_name}_${service}:" - local i=1 - # Use regular arrays instead of associative arrays - container_ids=() - container_names=() - - while read -r container_id; do - if [ -n "$container_id" ]; then - local container_name=$(docker inspect --format '{{.Name}}' "$container_id" | sed 's/\///') - container_ids[$i]=$container_id - container_names[$i]=$container_name - echo "[$i] ${container_names[$i]} (${container_ids[$i]})" - i=$((i+1)) - fi - done <<< "$running_containers" - - echo -e "\nPlease select a container number:" - read -r selection - - if [[ "$selection" =~ ^[0-9]+$ ]] && [ -n "${container_ids[$selection]}" ]; then - local selected_container=${container_ids[$selection]} - clear - echo "Showing logs for container: ${container_names[$selection]}" - echo "Press Ctrl+C or 'q' to return to container selection" - - # Start watching logs in the background - docker container logs -f "$selected_container" & - local log_pid=$! - - while true; do - read -r -n 1 input - if [[ $input == "q" ]]; then - kill $log_pid 2>/dev/null - wait $log_pid 2>/dev/null - break - fi - done - clear - else - echo "Error: Invalid selection" - sleep 2 - fi - else - # Single container case - local container_name=$(docker inspect --format '{{.Name}}' "$running_containers" | sed 's/\///') - echo "Showing logs for container: $container_name" - echo "Press Ctrl+C or 'q' to exit logs" - docker container logs -f "$running_containers" & - local log_pid=$! - - while true; do - read -r -n 1 input - if [[ $input == "q" ]]; then - kill $log_pid 2>/dev/null - wait $log_pid 2>/dev/null - break - fi - done - break - fi - done -} - -function viewLogs(){ - - ARG_SERVICE_NAME=$2 - if [ -z "$ARG_SERVICE_NAME" ]; - then - echo - echo "Select a Service you want to view the logs for:" - echo " 1) Web" - echo " 2) Space" - echo " 3) API" - echo " 4) Worker" - echo " 5) Beat-Worker" - echo " 6) Migrator" - echo " 7) Proxy" - echo " 8) Redis" - echo " 9) Postgres" - echo " 10) Minio" - echo " 11) RabbitMQ" - echo " 0) Back to Main Menu" - echo - read -p "Service: " DOCKER_SERVICE_NAME - - until (( DOCKER_SERVICE_NAME >= 0 && DOCKER_SERVICE_NAME <= 11 )); do - echo "Invalid selection. Please enter a number between 0 and 11." - read -p "Service: " DOCKER_SERVICE_NAME - done - - if [ -z "$DOCKER_SERVICE_NAME" ]; - then - echo "INVALID SERVICE NAME SUPPLIED" - else - case $DOCKER_SERVICE_NAME in - 1) viewSpecificLogs "web";; - 2) viewSpecificLogs "space";; - 3) viewSpecificLogs "api";; - 4) viewSpecificLogs "worker";; - 5) viewSpecificLogs "beat-worker";; - 6) viewSpecificLogs "migrator";; - 7) viewSpecificLogs "proxy";; - 8) viewSpecificLogs "plane-redis";; - 9) viewSpecificLogs "plane-db";; - 10) viewSpecificLogs "plane-minio";; - 11) viewSpecificLogs "plane-mq";; - 0) askForAction;; - *) echo "INVALID SERVICE NAME SUPPLIED";; - esac - fi - elif [ -n "$ARG_SERVICE_NAME" ]; - then - ARG_SERVICE_NAME=$(echo "$ARG_SERVICE_NAME" | tr '[:upper:]' '[:lower:]') - case $ARG_SERVICE_NAME in - web) viewSpecificLogs "web";; - space) viewSpecificLogs "space";; - api) viewSpecificLogs "api";; - worker) viewSpecificLogs "worker";; - beat-worker) viewSpecificLogs "beat-worker";; - migrator) viewSpecificLogs "migrator";; - proxy) viewSpecificLogs "proxy";; - redis) viewSpecificLogs "plane-redis";; - postgres) viewSpecificLogs "plane-db";; - minio) viewSpecificLogs "plane-minio";; - rabbitmq) viewSpecificLogs "plane-mq";; - *) echo "INVALID SERVICE NAME SUPPLIED";; - esac - else - echo "INVALID SERVICE NAME SUPPLIED" - fi -} - - - -function askForAction() { - # Rest of askForAction remains the same but use $stack_name instead of $STACK_NAME - local DEFAULT_ACTION=$1 - - if [ -z "$DEFAULT_ACTION" ]; then - echo - echo "Select an Action you want to perform:" - echo " 1) Deploy Stack" - echo " 2) Remove Stack" - echo " 3) View Stack Status" - echo " 4) Redeploy Stack" - echo " 5) Upgrade" - echo " 6) View Logs" - echo " 7) Exit" - echo - read -p "Action [3]: " ACTION - until [[ -z "$ACTION" || "$ACTION" =~ ^[1-6]$ ]]; do - echo "$ACTION: invalid selection." - read -p "Action [3]: " ACTION - done - - if [ -z "$ACTION" ]; then - ACTION=3 - fi - echo - fi - - if [ "$ACTION" == "1" ] || [ "$DEFAULT_ACTION" == "deploy" ]; then - deployStack - elif [ "$ACTION" == "2" ] || [ "$DEFAULT_ACTION" == "remove" ]; then - removeStack - elif [ "$ACTION" == "3" ] || [ "$DEFAULT_ACTION" == "status" ]; then - viewStatus - elif [ "$ACTION" == "4" ] || [ "$DEFAULT_ACTION" == "redeploy" ]; then - redeployStack - elif [ "$ACTION" == "5" ] || [ "$DEFAULT_ACTION" == "upgrade" ]; then - upgrade - elif [ "$ACTION" == "6" ] || [ "$DEFAULT_ACTION" == "logs" ]; then - viewLogs "$@" - elif [ "$ACTION" == "7" ] || [ "$DEFAULT_ACTION" == "exit" ]; then - exit 0 - else - echo "INVALID ACTION SUPPLIED" - fi -} - -# Initialize stack name at script start - -if [ -z "$stack_name" ]; then - readStackName -fi - -# Sync environment variables -if [ -f "$DOCKER_ENV_PATH" ]; then - DOCKERHUB_USER=$(getEnvValue "DOCKERHUB_USER" "$DOCKER_ENV_PATH") - APP_RELEASE=$(getEnvValue "APP_RELEASE" "$DOCKER_ENV_PATH") - - if [ -z "$DOCKERHUB_USER" ]; then - DOCKERHUB_USER=artifacts.plane.so/makeplane - updateEnvFile "DOCKERHUB_USER" "$DOCKERHUB_USER" "$DOCKER_ENV_PATH" - fi - - if [ -z "$APP_RELEASE" ]; then - APP_RELEASE=stable - updateEnvFile "APP_RELEASE" "$APP_RELEASE" "$DOCKER_ENV_PATH" - fi -fi - - -# Main execution -print_header -askForAction "$@"