diff --git a/.claude/rules/linting.md b/.claude/rules/linting.md new file mode 100644 index 0000000000..bb092a8e94 --- /dev/null +++ b/.claude/rules/linting.md @@ -0,0 +1,10 @@ +# Linting + +This project uses Prettier for code formatting. + +- All source files (`.ts`, `.tsx`, `.js`, `.jsx`, `.md`, `.json`, `.css`) must be formatted with Prettier before committing. +- `.mdx` is excluded (see `.prettierignore`): Prettier's markdown/mdx printer corrupts Docusaurus JSX-in-Markdown (Tabs/TabItem children, `{/* */}` comments). Format `.mdx` files by hand. +- Run `npx prettier --write ` after editing any file. +- Run `npm run format:check` to verify all files are formatted. +- The pre-commit hook runs lint-staged automatically, which formats staged files with Prettier. +- Do NOT skip the pre-commit hook with `--no-verify`. diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000000..e5b86a9571 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,11 @@ +{ + "hooks": { + "PostToolUse": [ + { + "matcher": "Edit|Write", + "fileMatcher": "\\.(ts|tsx|js|jsx|md|mdx|json|css)$", + "command": "npx prettier --write ${file}" + } + ] + } +} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index bb46d95c03..ed6838ad91 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -87,7 +87,6 @@ jobs: user_email: 41898282+github-actions[bot]@users.noreply.github.com cname: docs.defang.io - # Notify docs chatbot of new docs with curl command - name: Notify docs chatbot of new docs run: | diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000000..2312dc587f --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npx lint-staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..dd8a16cd8f --- /dev/null +++ b/.prettierignore @@ -0,0 +1,12 @@ +node_modules/ +build/ +.docusaurus/ +static/ +*.lock +# Docusaurus MDX mixes JSX with indented Markdown (Tabs/TabItem children, +# JSX comments). Prettier's markdown/mdx printer mis-parses that nesting - +# it reflows fenced code blocks into prose and escapes `*` inside `{/* */}` +# JSX comments - which corrupts content without erroring on some files and +# breaks the Docusaurus build on others. Leave .mdx unformatted until +# Prettier's MDX support handles this correctly. +*.mdx diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..638625c103 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": false, + "trailingComma": "all", + "tabWidth": 2, + "printWidth": 80 +} diff --git a/AGENTS.md b/AGENTS.md index 4c5ed5f7bc..b8f56852a3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,13 +1,26 @@ # Repository Guidelines ## Project Structure & Module Organization + The site is a Docusaurus 3 app. Author content in `docs/` (guides, CLI docs under `docs/cli/`) and `blog/` (Markdown posts). React-based customizations live in `src/` (`src/pages/index.tsx`, `src/components/`), while static assets and JSON feeds ship from `static/`. Build artifacts land in `build/` and should never be committed. Automation scripts sit in `scripts/`, notably `scripts/prebuild.sh`, which normalizes CLI docs and samples before every build. ## Build, Test, and Development Commands + Install dependencies with `npm install`. Use `npm run start` for local dev; it runs `scripts/prebuild.sh`, then serves the docs with hot reload. Execute `npm run build` for production-ready static output and `npm run serve` to smoke-test the generated `build/`. Run `npm run typecheck` to catch TS issues without compiling, and `npm run clear` if you need to reset the Docusaurus cache. ## Coding Style & Naming Conventions + Write prose in Markdown or MDX with frontmatter; use kebab-case filenames such as `docs/networking/private-endpoints.mdx`. React/TypeScript follows 2-space indentation, named exports when practical, and TypeScript types declared near component definitions. Tailwind utility classes are available; co-locate component styles via `.module.css` when utilities are insufficient. Keep metadata files (`_category_.json`) lean and descriptive. ## External Dependencies & Prebuild Notes + The prebuild step shells into `../defang/src/cmd/gendocs` (requires Go) and ingests `../samples/samples`. Keep those repositories updated locally, or vendor them into `defang-docs/defang` and `defang-docs/samples` when working in CI or preview branches. + +## Formatting + +This project uses Prettier for code formatting with a pre-commit hook (Husky + lint-staged). + +- Agents MUST run `npx prettier --write ` on any file they create or edit. +- Agents MUST NOT use `--no-verify` on commits. +- The pre-commit hook runs Prettier automatically on staged files, but agents should format before staging to catch issues early. +- Run `npm run format:check` to verify all files pass formatting. diff --git a/README.md b/README.md index 24841f6de9..e11b97cffa 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,11 @@ This website is built using [Docusaurus 2](https://docusaurus.io/), a modern sta ``` $ npm install ``` + Make sure that you've cloned the `defanglabs/defang` repo and `defanglabs/samples` repo in sibling directories. ``` -$ git clone https://github.com/DefangLabs/defang.git ../defang +$ git clone https://github.com/DefangLabs/defang.git ../defang $ git clone https://github.com/DefangLabs/samples.git ../samples ``` @@ -28,7 +29,7 @@ This command generates static content into the `build` directory and can be serv $ npm run start ``` -This command starts a local development server and opens up a browser window. +This command starts a local development server and opens up a browser window. ### Deployment diff --git a/babel.config.js b/babel.config.js index e00595dae7..bfd75dbdfc 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')], + presets: [require.resolve("@docusaurus/core/lib/babel/preset")], }; diff --git a/blog/2024-03-28-slackbot-sample.md b/blog/2024-03-28-slackbot-sample.md index 4ec72ed769..1bd8a2b93c 100644 --- a/blog/2024-03-28-slackbot-sample.md +++ b/blog/2024-03-28-slackbot-sample.md @@ -7,6 +7,7 @@ authors: raphael_titsworth_morin Hey folks! Today, I'm going to share one of our code samples that will show you how to deploy a simple Slack bot. If you're looking to connect a cloud service to Slack to publish status updates, or something else like that, this should help you get started. We'll walk through a step-by-step process of writing a Go program using the [github.com/slack-go/slack](https://github.com/slack-go/slack) library to interact with the Slack API and easily deploy it using Defang. {/* truncate */} + ## Prerequisites Before we dive into the details, let's make sure you have everything you need to get started: @@ -14,18 +15,18 @@ Before we dive into the details, let's make sure you have everything you need to 1. **Install Defang CLI:** Simplify your deployment process by installing the Defang CLI tool. Follow the instructions [here](/docs/intro/getting-started#install-the-defang-cli) to get it up and running quickly. 2. **Slack API Token:** Create a Slack App at https://api.slack.com/apps, granting it the necessary permissions, including the bot `chat:write` scope. -![screenshot of the slack admin UI showing the bot scopes](/img/slackbot-sample/scopes.png) + ![screenshot of the slack admin UI showing the bot scopes](/img/slackbot-sample/scopes.png) -3. **Install the app in your workspace:** You'll need to install the app in your workspace for it to work. Click the "Install to Workspace" button in the Slack admin UI to do this. Mine says "Reinstall" because I've already installed it. -![screenshot of the slack admin UI showing the install button](/img/slackbot-sample/install-app.png) +3. **Install the app in your workspace:** You'll need to install the app in your workspace for it to work. Click the "Install to Workspace" button in the Slack admin UI to do this. Mine says "Reinstall" because I've already installed it. + ![screenshot of the slack admin UI showing the install button](/img/slackbot-sample/install-app.png) -4. **Copy the Bot User OAuth Access Token:** This token will authenticate your Slackbot with the Slack API. -![screenshot of the slack admin UI showing the auth token field](/img/slackbot-sample/token.png) +4. **Copy the Bot User OAuth Access Token:** This token will authenticate your Slackbot with the Slack API. + ![screenshot of the slack admin UI showing the auth token field](/img/slackbot-sample/token.png) -5. **Invite the Bot to a Channel:** To enable your Slackbot, invite it to the desired channel using the `@` command. In the screenshot below, my bot's name actually starts with the word invite, but if your bot is called `mycoolbot` you would invite it with `@mycoolbot`. This ensures your Slackbot has the required permissions to interact with the channel. -![screenshot of the slack chat UI showing me inviting my bot](/img/slackbot-sample/invite.png) +5. **Invite the Bot to a Channel:** To enable your Slackbot, invite it to the desired channel using the `@` command. In the screenshot below, my bot's name actually starts with the word invite, but if your bot is called `mycoolbot` you would invite it with `@mycoolbot`. This ensures your Slackbot has the required permissions to interact with the channel. + ![screenshot of the slack chat UI showing me inviting my bot](/img/slackbot-sample/invite.png) -6. **Clone the Sample Code:** Clone the Defang repository and navigate to the `samples/golang/slackbot` directory. This directory contains the sample code for the Slackbot. +6. **Clone the Sample Code:** Clone the Defang repository and navigate to the `samples/golang/slackbot` directory. This directory contains the sample code for the Slackbot. ```bash git clone https://github.com/DefangLabs/defang @@ -44,9 +45,10 @@ Now that we have everything set up, let's dive into the deployment process. Foll defang config set --name SLACK_CHANNEL_ID --value your_slack_channel_id ``` -2. **Deploy the Slackbot:** Use the Defang CLI's `defang compose up` command to deploy. +2. **Deploy the Slackbot:** Use the Defang CLI's `defang compose up` command to deploy. ## Usage + With your Slackbot up and running, let's explore how to make the most of it. Let's send a POST request to the `/` endpoint with a JSON body containing the message you want to post to the Slack channel. Popular tools like cURL or Postman can help you send the request: ```bash @@ -56,4 +58,5 @@ curl 'https://raphaeltm-bot--8080.prod1.defang.dev/' \ ``` ## Takeaways + Congratulations! You've successfully deployed a Slackbot using Defang. If you deployed this as an internal service, you could use it to send status updates, alerts, or other important messages to your team. The possibilities are endless! diff --git a/blog/2024-05-01-may-product-updates.md b/blog/2024-05-01-may-product-updates.md index 5ed9cd05e5..fa0d08dc54 100644 --- a/blog/2024-05-01-may-product-updates.md +++ b/blog/2024-05-01-may-product-updates.md @@ -9,6 +9,7 @@ authors: defang_team Hey folks! There is a lot going on at Defang and we're excited to share our latest product updates with you. Over the last month, we've been working hard to improve the Defang system and make it even easier for you to deploy your applications. Here's a quick overview of what we've been up to: {/* truncate */} + ## npx defang We know a lot of you are using Defang for the first time. To make it easier to get started, we've added a new way to install the Defang CLI. Now you can use npx to run the CLI without installing it globally by running: @@ -26,17 +27,20 @@ Previously you could bring your own domain with - Defang - Go from idea to your favorite cloud in minutes. | Product Hunt - + + Over the past few months, our team has been working tirelessly to create a tool that transforms how developers develop, deploy, and debug cloud apps. With Defang, you can go from idea to your favorite cloud in minutes. 🚀 Today, we have the opportunity to showcase Defang to a global audience, and your support could make all the difference! If you already have a Product Hunt account, it's super easy. -* ✅ You can support our product if you like what we have built so far -* ✅ You can leave a comment and any feedback you may have (comments are great!) -* ✅ You can leave a review + +- ✅ You can support our product if you like what we have built so far +- ✅ You can leave a comment and any feedback you may have (comments are great!) +- ✅ You can leave a review Product Hunt launches are time sensitive as they last 24 hours, so if you have 30 seconds available right now, it would really mean a lot. diff --git a/blog/2025-02-18-model-context-protocol.md b/blog/2025-02-18-model-context-protocol.md index 2def05fda7..c1c41748a7 100644 --- a/blog/2025-02-18-model-context-protocol.md +++ b/blog/2025-02-18-model-context-protocol.md @@ -13,7 +13,7 @@ tags: MCP, Model Context Protocol, Chatbot, - Docker + Docker, ] authors: defang_team --- @@ -39,8 +39,8 @@ Let’s go over the structure of the application in a local environment. ### General Overview 1. There are two containerized services, Service 1 and Service 2, that sit on the local machine. - - Service 1 contains a custom-built web server that interacts with an MCP Client. - - Service 2 contains an MCP Server from Docker as a base image for the container, and a custom-built MCP Client we created for interacting with the MCP Server. + - Service 1 contains a custom-built web server that interacts with an MCP Client. + - Service 2 contains an MCP Server from Docker as a base image for the container, and a custom-built MCP Client we created for interacting with the MCP Server. 2. We have a browser on our local machine, which interacts with the web server in Service 1. 3. The MCP Server in Service 2 is able to access tools from either a cloud or on our local machine. This configuration is included as a part of the Docker MCP image. 4. The MCP Client in Service 2 interacts with the Anthropic API and the web server. @@ -78,6 +78,7 @@ The base image for Service 1 is the `node:bookworm-slim` image. We construct the The base image for Service 2 is the Docker `mcp/time` image. Since both the MCP Client and Server run in a virtual environment, we activate a `venv` command in the Dockerfile for Service 2 and create a `run.sh` shell script that runs the file containing the MCP Client and Server connection code. We then add the shell script as an entry point command for the container. ### Compose File + To define Services 1 and 2 as Docker containers, we’ve written a `compose.yaml` file in the root directory, as shown below. ```yaml diff --git a/blog/2025-03-14-deploying-defang-with-defang-part-1.md b/blog/2025-03-14-deploying-defang-with-defang-part-1.md index aec6574392..2828bffebb 100644 --- a/blog/2025-03-14-deploying-defang-with-defang-part-1.md +++ b/blog/2025-03-14-deploying-defang-with-defang-part-1.md @@ -45,7 +45,7 @@ As a result, we reached a point where we no longer needed custom infrastructure ## **What Changed?** -- **Previously**: GitHub Actions ran infra-as-code scripts to provision databases, manage DNS, and define services *separately from the Docker Compose file we used for local dev* +- **Previously**: GitHub Actions ran infra-as-code scripts to provision databases, manage DNS, and define services _separately from the Docker Compose file we used for local dev_ - **Now**: Our [**Defang GitHub Action**](https://github.com/marketplace/actions/defang-deployment-action) targets normal Compose files and deploys everything, using secrets and variables managed in GitHub Actions environments. - **Result**: We **eliminated hundreds of lines of Infra-as-Code**, making our deployment leaner and easier to manage and reducing the differences between running the Portal locally and running it in the cloud. @@ -81,9 +81,9 @@ This wasn’t just about reducing complexity—it was also a validation exercise By transitioning to **fully Compose-based deployments**, we: -* ✅ **Eliminated hundreds of lines of Infra-as-Code** -* ✅ **Simplified configuration management** with secure, environment-aware secrets handling -* ✅ **Streamlined CI/CD** with a lightweight GitHub Actions workflow -* ✅ **Simplified DNS and cert management** +- ✅ **Eliminated hundreds of lines of Infra-as-Code** +- ✅ **Simplified configuration management** with secure, environment-aware secrets handling +- ✅ **Streamlined CI/CD** with a lightweight GitHub Actions workflow +- ✅ **Simplified DNS and cert management** Every sample project we built, every conversation we had with developers, and every challenge we encountered with the Portal helped us get to this point where we could focus on closing the gaps last few gaps to deploying everything from a Compose file. diff --git a/blog/2025-03-15-feb-product-updates.md b/blog/2025-03-15-feb-product-updates.md index d1f4010f58..f9feb0bbb5 100644 --- a/blog/2025-03-15-feb-product-updates.md +++ b/blog/2025-03-15-feb-product-updates.md @@ -1,15 +1,6 @@ --- title: February 2025 Defang Compose Update -tags: - [ - Cloud, - DevOps, - Pulumi, - Compose, - OpenAuth, - Portal, - Events, - ] +tags: [Cloud, DevOps, Pulumi, Compose, OpenAuth, Portal, Events] authors: defang_team --- @@ -23,7 +14,7 @@ Well, that went by quick! Seems like it was just a couple of weeks ago that we p **Events and Social Media** -February was an exciting month for the Defang team as we continued to engage with the developer community and showcase what’s possible with Defang. We sponsored and demo’ed at the [DevTools Vancouver meetup](https://lu.ma/devtools3), as well as sponsored the [Vancouver.dev IRL: Building AI Startups](https://lu.ma/vandevirl2) event. Also, at the AWS Startup Innovation Showcase in Vancouver, our CTO [Lio](https://www.linkedin.com/in/lionello/) [demonstrated](https://www.linkedin.com/feed/update/urn:li:activity:7299861024780808193) how Defang makes it effortless to deploy secure, scalable, and cost-efficient serverless apps on AWS! And finally, we had a great response to our [LinkedIn post](https://www.linkedin.com/posts/defanglabs_mcp-docker-anthropic-activity-7298043665883508736-i4dd?utm_source=share&utm_medium=member_desktop&rcm=ACoAAAAQqiEBLsVLYYAzEmBFB9oIl31nQ7kDII0) on the Model Context Protocol, catching the attention of many observers, including some of our key partners. +February was an exciting month for the Defang team as we continued to engage with the developer community and showcase what’s possible with Defang. We sponsored and demo’ed at the [DevTools Vancouver meetup](https://lu.ma/devtools3), as well as sponsored the [Vancouver.dev IRL: Building AI Startups](https://lu.ma/vandevirl2) event. Also, at the AWS Startup Innovation Showcase in Vancouver, our CTO [Lio](https://www.linkedin.com/in/lionello/) [demonstrated](https://www.linkedin.com/feed/update/urn:li:activity:7299861024780808193) how Defang makes it effortless to deploy secure, scalable, and cost-efficient serverless apps on AWS! And finally, we had a great response to our [LinkedIn post](https://www.linkedin.com/posts/defanglabs_mcp-docker-anthropic-activity-7298043665883508736-i4dd?utm_source=share&utm_medium=member_desktop&rcm=ACoAAAAQqiEBLsVLYYAzEmBFB9oIl31nQ7kDII0) on the Model Context Protocol, catching the attention of many observers, including some of our key partners. We are eager to see what you deploy with Defang. Join our [Discord](http://s.defang.io/discord) to ask any questions, see what others are building, and share your own experience with Defang. And stay tuned for more to come in March! diff --git a/blog/2025-03-26-deploying-defang-with-defang-part-2.md b/blog/2025-03-26-deploying-defang-with-defang-part-2.md index a932d3ca13..0a3eec2c4d 100644 --- a/blog/2025-03-26-deploying-defang-with-defang-part-2.md +++ b/blog/2025-03-26-deploying-defang-with-defang-part-2.md @@ -22,7 +22,7 @@ Our original site was a Next.js app using [static exports](https://nextjs.org/do That meant static hosting wouldn't cut it. So we decided to run the site as an app in a container. -That being said, our learnings from the previous setup *are* being used to develop the capabilities of Defang. We're using the experience to make sure that Defang can handle the deployment of static sites as well as dynamic ones. We'll keep you updated when that's ready. +That being said, our learnings from the previous setup _are_ being used to develop the capabilities of Defang. We're using the experience to make sure that Defang can handle the deployment of static sites as well as dynamic ones. We'll keep you updated when that's ready. {/* truncate */} @@ -35,20 +35,22 @@ We already deploy our other services with Defang using Compose files. In fact, t Some things we had to change: **Adding [ports](/docs/concepts/compose#ports) to the Compose file**: + ```yaml - ports: - - mode: ingress - target: 3000 - published: 3000 +ports: + - mode: ingress + target: 3000 + published: 3000 ``` **Adding [domain](/docs/concepts/domains) info the Composer file**: + ```yaml - domainname: defang.io - networks: - default: - aliases: - - www.defang.io +domainname: defang.io +networks: + default: + aliases: + - www.defang.io ``` One other hiccup was that we used to do www to non-www redirects using S3. There are a few ways to switch that up, but for the time being we decided to use Next.js middleware. @@ -62,12 +64,15 @@ Pretty soon after that, the site was up and running in an AWS account—with TLS Deploying the website wasn't just a checkbox—it helped surface real-world pain points and ideas for improvement. ### 1. Static Assets Still Need CDNs + Even though the site is dynamic now, we still want assets like `/_next/static` to load quickly from a CDN. This made it clear that CDN support—like CloudFront integration—should be easier to configure in Defang. That’s now on our roadmap. That's also going to be useful for other frameworks that use similar asset paths, like Django. ### 2. Next.js Env Vars Can Be Tricky in Containers + Next.js splits env vars between build-time and runtime, and the rules aren’t always obvious. Some need to be passed as build args, and others as runtime envs. That made us think harder about how Defang could help clarify or streamline this for developers—even if we can’t change that aspect of Next.js itself. ### 3. Redirects and Rewrites + We had to add a middleware to handle www to non-www redirects. This is a common need, so we're keeping an eye on how we can make this easier to deal with in Defang projects. These are the kinds of things we only notice by using Defang on real-world projects. @@ -84,4 +89,3 @@ Our site now runs like the rest of our infrastructure: - Deployed with Defang Stay tuned for the next post in the series—because this is just one piece of the puzzle. - diff --git a/blog/2025-04-10-easiest-way-to-deploy-django.md b/blog/2025-04-10-easiest-way-to-deploy-django.md index 6f9183253d..a5be4d2262 100644 --- a/blog/2025-04-10-easiest-way-to-deploy-django.md +++ b/blog/2025-04-10-easiest-way-to-deploy-django.md @@ -46,10 +46,13 @@ This runs things with autoreloading so you can iterate on the Django app, all wh ## Application Features ### Real-time Chat + Using Django Channels and Redis, users can engage in real-time conversations within chat rooms. ### Background Moderation Tasks + The worker service runs independently, handling moderation tasks asynchronously. It uses NLTK to: + - Check for profanity. - Perform sentiment analysis. - Automatically flag negative or inappropriate messages. @@ -60,7 +63,6 @@ This decouples resource-intensive tasks from the main API server, ensuring optim The Django admin is setup to quickly visualize messages and their moderation status. Access it at `/admin` with your superuser credentials: username `admin` and password `admin` setup by default when you first run or deploy. - ## Deploying with Defang Deploying multi-service applications to cloud providers traditionally involves complex infrastructure setup, including configuring ECS clusters, security groups, networking, and more. Defang simplifies this significantly. @@ -85,6 +87,7 @@ defang compose up ``` Defang automatically: + - Builds Docker containers. - Sets up required services. - Manages networking and provisioning. @@ -96,6 +99,7 @@ Once deployed, your app is accessible via a public URL provided by Defang, which To deploy directly into your AWS account (or other [supported providers](/docs/providers)): 1. Set your cloud provider: + > In my case, I use an AWS Profile, but you should be able to use [any methods supported by the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html) ```bash @@ -123,6 +127,7 @@ Defang handles provisioning managed services (RDS for Postgres, ElastiCache for ## Cloud Deployment Results Post-deployment, your Django app infrastructure includes (among other things): + - **Managed Postgres**: AWS RDS instance. - **Managed Redis**: AWS ElastiCache instance. - **Containers**: ECS services with load balancers and DNS configured. @@ -130,6 +135,7 @@ Post-deployment, your Django app infrastructure includes (among other things): ## Why Use Defang? Defang simplifies complex cloud deployments by: + - Automatically provisioning managed cloud resources. - Securely handling sensitive configurations. - Providing seamless container orchestration without manual infrastructure setup. diff --git a/blog/2025-06-16-crew-ai-sample.md b/blog/2025-06-16-crew-ai-sample.md index b76ffb1120..58e6ebb2d3 100644 --- a/blog/2025-06-16-crew-ai-sample.md +++ b/blog/2025-06-16-crew-ai-sample.md @@ -25,11 +25,11 @@ Imagine you're building a system. It might use multiple LLM calls. It might do c ## Architecture at a Glance -Behind the scenes, the workflow is clean and powerful. The browser connects via [WebSockets to our app using Django Channels](https://channels.readthedocs.io/en/latest/deploying.html#http-and-websocket). Heavy work is pushed to a [Celery worker](https://docs.celeryq.dev/en/stable/). That worker generates an [embedding](https://en.wikipedia.org/wiki/Embedding_(machine_learning)), checks [Postgres](https://www.postgresql.org/) with [pgvector](https://github.com/pgvector/pgvector) for a match, and either returns the summary or, if there’s no hit, fires up a [CrewAI agent](https://www.crewai.com/) to generate one. Every update streams back through [Redis](https://redis.io/) and Django Channels so users get progress in real time. +Behind the scenes, the workflow is clean and powerful. The browser connects via [WebSockets to our app using Django Channels](https://channels.readthedocs.io/en/latest/deploying.html#http-and-websocket). Heavy work is pushed to a [Celery worker](https://docs.celeryq.dev/en/stable/). That worker generates an [embedding](), checks [Postgres](https://www.postgresql.org/) with [pgvector](https://github.com/pgvector/pgvector) for a match, and either returns the summary or, if there’s no hit, fires up a [CrewAI agent](https://www.crewai.com/) to generate one. Every update streams back through [Redis](https://redis.io/) and Django Channels so users get progress in real time. Architecture -Durable state lives in Postgres and Redis. Model services ([LLMs](https://en.wikipedia.org/wiki/LLM) and embeddings) are fully swappable, so you can upgrade to different models in the cloud or localize with the [Docker Model Runner](https://docs.docker.com/compose/how-tos/model-runner/) without rewriting the full stack. +Durable state lives in Postgres and Redis. Model services ([LLMs](https://en.wikipedia.org/wiki/LLM) and embeddings) are fully swappable, so you can upgrade to different models in the cloud or localize with the [Docker Model Runner](https://docs.docker.com/compose/how-tos/model-runner/) without rewriting the full stack. ## Under the Hood: The Services @@ -51,7 +51,7 @@ The Celery worker is where the magic happens. It takes requests off the queue, g ### LLM and Embedding Services -Thanks to Docker Model Runner, the LLM and embedding services run as containerized, OpenAI-compatible HTTP endpoints. Want to switch to a different model? Change a single line in your compose file. Environment variables like `LLM_URL` and `EMBEDDING_MODEL` are injected for you—no secret sharing or hard-coding required. +Thanks to Docker Model Runner, the LLM and embedding services run as containerized, OpenAI-compatible HTTP endpoints. Want to switch to a different model? Change a single line in your compose file. Environment variables like `LLM_URL` and `EMBEDDING_MODEL` are injected for you—no secret sharing or hard-coding required. ### CrewAI Workflows diff --git a/blog/2025-08-22-agentic-apps.md b/blog/2025-08-22-agentic-apps.md index 47ecc5a09e..898fb62708 100644 --- a/blog/2025-08-22-agentic-apps.md +++ b/blog/2025-08-22-agentic-apps.md @@ -47,7 +47,6 @@ Defang works seamlessly with leading agentic frameworks. Try them out with our r - [LangGraph](https://github.com/DefangLabs/samples/tree/main/samples/agentic-langgraph) - workflow sample that defines and controls multi-step agentic graphs with LangChain. - [Agentic Strands](https://github.com/DefangSamples/sample-agentic-strands-template/tree/main) - A Strands Agent application. - More framework templates coming soon. ## Why It Matters diff --git a/blog/2025-09-25-heroku-migration.md b/blog/2025-09-25-heroku-migration.md index c5f5ff4a0f..cb67e60620 100644 --- a/blog/2025-09-25-heroku-migration.md +++ b/blog/2025-09-25-heroku-migration.md @@ -2,19 +2,19 @@ title: "Beyond Heroku: Owning Your Deployments" description: "Why scaling off Heroku matters, what happens during a migration, and how the Defang CLI makes the transition to AWS painless." slug: heroku-to-aws -tags: [Heroku, AWS, Migration, PaaS vs IaaS, Cloud, DevOps, Defang, Docker Compose] +tags: + [Heroku, AWS, Migration, PaaS vs IaaS, Cloud, DevOps, Defang, Docker Compose] authors: defang_team date: 2025-09-25 --- - When you launch a new app, convenience rules. Platforms like Heroku offer a beautifully simple way to push code and see it live in minutes. You don’t need to think about servers, networks or databases. You just deploy. That’s why so many of us start there. But convenience has a cost. As your product grows, you want more control over performance and security. You want to integrate your own services, tune the infrastructure and optimize your spend. Heroku’s dyno‑based pricing, which starts around $25/month for a modest dyno and climbs to hundreds of dollars for high‑performance dynos, can become prohibitive for serious production workloads. And while Heroku abstracts away the underlying cloud, that abstraction also means you can’t fine‑tune the way your application runs. -This trade‑off eventually becomes untenable. Teams need the simplicity of a platform like Heroku *and* the power and trust of running inside their own AWS account. This post unpacks why migrating off Heroku matters, highlights the friction points when you try to move to AWS yourself, and shows how the **Defang CLI** bridges the gap. +This trade‑off eventually becomes untenable. Teams need the simplicity of a platform like Heroku _and_ the power and trust of running inside their own AWS account. This post unpacks why migrating off Heroku matters, highlights the friction points when you try to move to AWS yourself, and shows how the **Defang CLI** bridges the gap. {/* truncate */} @@ -32,25 +32,25 @@ AWS uses a pay‑as‑you‑go model: you pay for the exact resources you use, w For many teams, Heroku is the right starting point. But there are clear inflection points when it makes sense to graduate: -* **Escalating costs**. -As your user base grows, dyno bills rise exponentially. At some point, the predictable price premium no longer justifies the convenience. +- **Escalating costs**. + As your user base grows, dyno bills rise exponentially. At some point, the predictable price premium no longer justifies the convenience. -* **Performance and scalability demands**. -High‑traffic applications need flexible scaling and the ability to choose instance sizes and storage types. Heroku’s dyno types can be limiting for CPU‑ or memory‑intensive workloads. +- **Performance and scalability demands**. + High‑traffic applications need flexible scaling and the ability to choose instance sizes and storage types. Heroku’s dyno types can be limiting for CPU‑ or memory‑intensive workloads. -* **Compliance and data sovereignty**. -Customers in regulated industries often require apps to run in their own cloud account and under their own compliance controls. +- **Compliance and data sovereignty**. + Customers in regulated industries often require apps to run in their own cloud account and under their own compliance controls. -* **Customization**. -You might need to integrate bespoke networking, private databases or other services not available as Heroku add‑ons. AWS’s vast ecosystem of more than 240 services makes these integrations possible. +- **Customization**. + You might need to integrate bespoke networking, private databases or other services not available as Heroku add‑ons. AWS’s vast ecosystem of more than 240 services makes these integrations possible. Yet the path off Heroku isn’t trivial. Re‑platforming often means rewriting your application to use AWS services directly, building new CI/CD pipelines, managing IAM roles and provisioning infrastructure by hand. That’s a big lift for developers who just want to ship features. ## **Migration in minutes: how the Defang CLI works** -In our recent video (“*How to migrate from Heroku to AWS in 5 minutes!*”), we demonstrated a Django \+ Postgres app running on Heroku. The goal: deploy it into our own AWS account without rewriting anything. Here’s how it works: +In our recent video (“_How to migrate from Heroku to AWS in 5 minutes!_”), we demonstrated a Django \+ Postgres app running on Heroku. The goal: deploy it into our own AWS account without rewriting anything. Here’s how it works: -**Import your Heroku app.** +**Import your Heroku app.** After installing and logging into the Defang CLI, run: `defang init` @@ -59,10 +59,10 @@ Then select `Migrate from Heroku`. Defang connects to the Heroku API, inspects your app’s dynos, add‑ons and configuration variables, and generates a Docker Compose file. It translates Procfile commands into services and records dependencies like Postgres and Redis. -**Review the Compose file.** +**Review the Compose file.** You should always examine the generated compose.yaml. You can adjust ports or remove unnecessary services. In the demo we changed the exposed port to 8000 and confirmed everything looked reasonable. -**Select your cloud.** +**Select your cloud.** We authenticated against AWS and selected a profile (`AWS_PROFILE=defang-lab`). In Defang, you set a provider with an environment variable `DEFANG_PROVIDER=aws`. You can either export these, or pass them to each command: @@ -73,17 +73,17 @@ Or you can set them as environment variables: `export AWS_PROFILE=defang-lab DEFANG_PROVIDER=aws` -**Set your secrets.** +**Set your secrets.** You then run `defang config set` to provide any secrets (database user, password, database name) that were previously stored in Heroku. These secrets are encrypted at rest and passed securely to your services deployment. -**Deploy with one command.** +**Deploy with one command.** Finally, execute: `defang up` Defang provisions an ECS cluster, RDS database, VPC, security groups, DNS records, load balancer, and more for your application. It also provisions a release service to handle migrations and brings up your web service once the database is ready. Eventually you get a public URL for your working application. -**Verify and scale.** +**Verify and scale.** The entire migration took roughly five minutes from start to finish, with zero changes to application code. Instead of rewriting our Django settings or learning the intricacies of ECS, we let Defang automate the heavy lifting. @@ -95,10 +95,10 @@ As we covered earlier, Heroku's dyno pricing can quickly escalate from $25/month More importantly, you gain access to AWS's full ecosystem of 240+ services. Need a specific instance type for CPU-intensive workloads? Custom networking for multi-region deployments? Advanced monitoring and logging? On Heroku, you're limited to what's available in their add-on marketplace. On AWS, you can integrate any service, tune performance at the infrastructure level, and architect solutions that simply aren't possible on a PaaS. -For some teams, there's also the benefit of deploying into customer cloud accounts for compliance and data sovereignty requirements. +For some teams, there's also the benefit of deploying into customer cloud accounts for compliance and data sovereignty requirements. Defang bridges this gap by giving you Heroku-like simplicity with AWS power. ## **Try it yourself** -If your team is outgrowing Heroku or you need to bring your application into your customers’ cloud, give our migration workflow a spin. Install the Defang CLI, run defang init migrate-from-heroku, and watch your app come to life in AWS. You can find more details in our [official migration guide](https://docs.defang.io/docs/tutorials/migrating-from-heroku). We’d love to hear what you deploy and what features you’d like us to add next. \ No newline at end of file +If your team is outgrowing Heroku or you need to bring your application into your customers’ cloud, give our migration workflow a spin. Install the Defang CLI, run defang init migrate-from-heroku, and watch your app come to life in AWS. You can find more details in our [official migration guide](https://docs.defang.io/docs/tutorials/migrating-from-heroku). We’d love to hear what you deploy and what features you’d like us to add next. diff --git a/blog/2026-07-15-ai-native-enterprise.md b/blog/2026-07-15-ai-native-enterprise.md index 454f46b5f5..6df82e98ad 100644 --- a/blog/2026-07-15-ai-native-enterprise.md +++ b/blog/2026-07-15-ai-native-enterprise.md @@ -35,19 +35,19 @@ The goal isn't to replace engineers. It's to redesign engineering so that humans Like every major technology transition, this transformation happens gradually. At Defang, we think of it as the **AI-Native Maturity Model**. -| Stage | Maturity | Engineering Organization | -| --- | --- | --- | -| 0 | Resistant | AI is prohibited or ignored. Engineering remains entirely human-driven. | -| 1 | Curious | Individual engineers experiment with AI independently. | -| 2 | Assisted | AI improves individual productivity through coding assistants and personal AI tools. | -| 3 | Managed | Organization-wide AI standards, governance, and approved tooling are established. | -| 4 | Integrated | AI is embedded throughout the software development lifecycle, from design and coding to testing, documentation, and reviews. | -| 5 | Delegated | Well-defined engineering tasks are delegated to specialized AI agents operating under human supervision. | -| 6 | Operational | AI agents become trusted participants in deployments, operations, monitoring, and enterprise workflows. | -| 7 | Collaborative | Engineering teams coordinate multiple specialized AI agents across development, operations, security, testing, and support. | -| 8 | AI-First | Engineering workflows are redesigned around humans and AI agents working together from the outset. | -| 9 | Adaptive | AI continuously improves engineering systems and processes while humans provide strategy, governance, and oversight. | -| 10 | AI-Native | Humans and AI agents function as a single adaptive engineering organization that continuously learns and evolves. | +| Stage | Maturity | Engineering Organization | +| ----- | ------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| 0 | Resistant | AI is prohibited or ignored. Engineering remains entirely human-driven. | +| 1 | Curious | Individual engineers experiment with AI independently. | +| 2 | Assisted | AI improves individual productivity through coding assistants and personal AI tools. | +| 3 | Managed | Organization-wide AI standards, governance, and approved tooling are established. | +| 4 | Integrated | AI is embedded throughout the software development lifecycle, from design and coding to testing, documentation, and reviews. | +| 5 | Delegated | Well-defined engineering tasks are delegated to specialized AI agents operating under human supervision. | +| 6 | Operational | AI agents become trusted participants in deployments, operations, monitoring, and enterprise workflows. | +| 7 | Collaborative | Engineering teams coordinate multiple specialized AI agents across development, operations, security, testing, and support. | +| 8 | AI-First | Engineering workflows are redesigned around humans and AI agents working together from the outset. | +| 9 | Adaptive | AI continuously improves engineering systems and processes while humans provide strategy, governance, and oversight. | +| 10 | AI-Native | Humans and AI agents function as a single adaptive engineering organization that continuously learns and evolves. | Today, most organizations are somewhere between the **Assisted** and **Integrated** stages. The next wave of competitive advantage won't come from adopting another AI tool. It will come from progressing through the higher stages of this maturity model. diff --git a/docs/concepts/compose.md b/docs/concepts/compose.md index 8eb2e433c9..9d1a031e26 100644 --- a/docs/concepts/compose.md +++ b/docs/concepts/compose.md @@ -18,6 +18,7 @@ You can create a `compose.yaml` file in the root of your project, or use the [`d When you run `defang compose up`, Defang will read your `compose.yaml` file and [deploy](./deployments.md) the services named in that file to the cloud. ## Example of a Compose File + Here is a basic `compose.yaml` file that contains all the required properties for deployment in Defang. ```yaml @@ -34,9 +35,11 @@ services: ``` ## Compose Top-level Properties + Here are a list of top-level properties of the [Compose specification](https://docs.docker.com/compose/compose-file/) that Defang supports when writing a `compose.yaml` file. ### `services` + (Required) The services defined in your application. @@ -52,6 +55,7 @@ Defang identifies a service based on your username, project name, and the servic ::: ### `networks` + (Optional) The networks defined in your application. This is commonly added together with a [service-level `networks`](#networks-1) property. @@ -64,6 +68,7 @@ networks: See our [Networking](/docs/concepts/networking) page for more. ### `volumes` + (Not yet supported) The volume mounts for a container, reusable across services. This feature is not currently supported by Defang. @@ -78,55 +83,62 @@ Defang does not support the `secrets` top-level property. Please read our [Confi ::: ## Compose Service-level Properties + Here are a list of service-level properties of the [Compose specification](https://docs.docker.com/compose/compose-file/) that Defang supports when writing a `compose.yaml` file. :::tip Service-level means inside your `service`. A service-level property called `build` would look like: + ```yaml service: build: … ``` Note that in your Compose file, you will need a top-level property called `services` to contain all of your services. For example: + ```yaml services: service: build: … ``` + ::: ### `build` + (Required, unless `image` is defined) The [build configuration](https://github.com/compose-spec/compose-spec/blob/main/build.md). This property describes how to create an OCI container for this service. ```yaml - build: - context: . - dockerfile: ./Dockerfile - args: - NEXT_PUBLIC_VERSION: "1.2.3" +build: + context: . + dockerfile: ./Dockerfile + args: + NEXT_PUBLIC_VERSION: "1.2.3" ``` ### `image` + (Required, unless `build` is defined) [This property](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#image) describes the image from which your container should start. ```yaml - image: nginx:latest +image: nginx:latest ``` ### `ports` + (Optional, but required if you want to access the service from outside the container) The ports to expose. The default port mode is `ingress`. ```yaml - ports: - - mode: ingress - target: 80 - published: 80 +ports: + - mode: ingress + target: 80 + published: 80 ``` :::info @@ -134,56 +146,65 @@ Defang ignores `published` ports in production. As such, it is common to make `t ::: ### `command` + (Optional) The command which will be run to start your service. If left out, the command from the Docker image will be used. ```yaml - command: nginx -g 'daemon off;' +command: nginx -g 'daemon off;' ``` ### `deploy` + (Optional) The [Deploy Specification](https://github.com/compose-spec/compose-spec/blob/main/deploy.md) describes the runtime constraints and requirements for how your services will be deployed and managed across different environments (e.g. memory reservations, replicas, number of CPUs, etc.). ```yaml - deploy: - replicas: 1 - reservations: - cpus: '0.5' - memory: 256M +deploy: + replicas: 1 + reservations: + cpus: "0.5" + memory: 256M ``` ### `depends_on` + (Limited support) This property describes startup dependencies between services. This feature currently has limited supported by Defang: dependency on a managed service does not wait for the managed service provisioning to complete. ```yaml - # depends_on: - # - db +# depends_on: +# - db ``` ### `environment` + (Optional) The environment variables to set. + ```yaml - environment: - DATABASE_USER: someuser +environment: + DATABASE_USER: someuser ``` + :::info For sensitive environment variables (or secret values), you should list the variable's name with a blank or `null` value, and then securely set their actual value with `defang config` in the CLI. See our [Configuration page](/docs/concepts/configuration) for more. - For example: +For example: + ```yaml - - DATABASE_USER=someuser # env var loaded with this literal value - - DATABASE_PASSWORD # env var loaded using defang config +- DATABASE_USER=someuser # env var loaded with this literal value +- DATABASE_PASSWORD # env var loaded using defang config ``` + ::: ### `healthcheck` + (Optional, but required for healthchecks on services with a published port) [This property](https://github.com/compose-spec/compose-spec/blob/main/05-services.md#healthcheck) describes a check that will be run to determine whether or not a service's containers are "healthy". It works in the same way, and has the same default values, as the [HEALTHCHECK Dockerfile instruction](https://docs.docker.com/engine/reference/builder/#healthcheck) set by the service's Docker image. Your Compose file can override the values set in the Dockerfile. @@ -195,59 +216,63 @@ When using Defang, your Compose file must have a healthcheck if you want to expo ::: ```yaml - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:8080/"] - interval: 30s - timeout: 90s - retries: 3 +healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/"] + interval: 30s + timeout: 90s + retries: 3 ``` or ```yaml - healthcheck: - test: ["CMD", "wget", "--spider", "http://localhost:8080/"] - interval: 30s - timeout: 90s - retries: 3 +healthcheck: + test: ["CMD", "wget", "--spider", "http://localhost:8080/"] + interval: 30s + timeout: 90s + retries: 3 ``` ### `networks` + (Optional) The service network configuration. By default, Compose will add services to the `default` network, which has external connectivity. You can also add services to private networks. To avoid warnings, you should add them to the [top-level `networks`](#networks) property as well. ```yaml - networks: - default: # when not specified, services are assigned to the "default" network +networks: + default: # when not specified, services are assigned to the "default" network ``` You can also assign an alias for a network by using `aliases`, as seen below: + ```yaml - networks: - default: - aliases: - - app +networks: + default: + aliases: + - app ``` See our [Networking](/docs/concepts/networking) page for more. ### `restart` + (Optional, but highly recommended) The restart mode for a container. Defaults to `unless-stopped` unless otherwise specified. ```yaml - restart: unless-stopped +restart: unless-stopped ``` ### `volumes` + (Not yet supported) The volume mounts for a container, specific to a service. This feature is not currently supported by Defang. ```yaml - # volumes: - # - "./backend:/app" +# volumes: +# - "./backend:/app" ``` diff --git a/docs/concepts/configuration.md b/docs/concepts/configuration.md index 26ca40bb68..b7ee78bfd9 100644 --- a/docs/concepts/configuration.md +++ b/docs/concepts/configuration.md @@ -21,6 +21,7 @@ You can use sensitive config by specifying them in the `environment` section of Either one of list notation or map notation is acceptable for defining your environment variable(s). See below for an example of each. #### With List Notation + ```yaml services: service1: @@ -30,6 +31,7 @@ services: ``` #### With Map Notation + ```yaml services: service1: @@ -61,8 +63,8 @@ service: - USER_NAME // configuration variable - CONNECT=dbservice:${USER_NAME}:${USER_PASSWORD}@example.com:9876 ``` -In the example above, if we assume the value of the configuration variable ***USER_PASSWORD*** is *password* then the value assigned to ***CONNECT*** will resolve to *dbservice:alice:password@example.com:9876* +In the example above, if we assume the value of the configuration variable _**USER_PASSWORD**_ is _password_ then the value assigned to _**CONNECT**_ will resolve to _dbservice:alice:password@example.com:9876_ During `defang compose up` all variable references will be replaced with the actual value and made available in the container. If any referenced variable is not found the `defang compose up` command will be canceled. @@ -81,6 +83,7 @@ Defang does pass environment variables from the shell into your services. Enviro Environment variables are resolved in order of precedence, with the highest precedence value taking priority. For example, if you have a variable `DATABASE_URL` set in both a dotenv file and in Defang config, the value from Defang config will be used. ## Using Config with Pulumi + In Defang, using config with [Pulumi](./pulumi.md) gives you the advantage of being able to manage your environment variables across different environments using Pulumi stacks. :::tip @@ -106,9 +109,9 @@ Please note that while Defang supports setting sensitive config, it does not sup ## Supported Providers -| Provider | Config Support | -|----------------|:--------------:| -| AWS | ✅ | -| Azure | ✅ | -| DigitalOcean | ✅ | -| GCP | ✅ | +| Provider | Config Support | +| ------------ | :------------: | +| AWS | ✅ | +| Azure | ✅ | +| DigitalOcean | ✅ | +| GCP | ✅ | diff --git a/docs/concepts/defang-byoc.md b/docs/concepts/defang-byoc.md index ebc3f6eeb2..0bdca80289 100644 --- a/docs/concepts/defang-byoc.md +++ b/docs/concepts/defang-byoc.md @@ -5,7 +5,7 @@ description: Defang allows you deploy services, defined as containers, to your o # Defang BYOC -Defang aims to make it easier to deploy your services to the cloud. Specifically, Defang's goal is to make it easier to deploy your workloads to your *own* cloud accounts. We refer to this as bring-your-own-cloud (BYOC). +Defang aims to make it easier to deploy your services to the cloud. Specifically, Defang's goal is to make it easier to deploy your workloads to your _own_ cloud accounts. We refer to this as bring-your-own-cloud (BYOC). :::info[Pricing] BYOC is available on all tiers (Starter, Pro, Enterprise). The Starter tier (free) includes 1 cloud account. Pro ($49/mo) includes 1 cloud account with additional deployment modes. Enterprise ($499/mo) includes 3 cloud accounts with fleet management capabilities. diff --git a/docs/concepts/defang-playground.md b/docs/concepts/defang-playground.md index f4bdd173b9..c0c225f1a8 100644 --- a/docs/concepts/defang-playground.md +++ b/docs/concepts/defang-playground.md @@ -13,7 +13,7 @@ Defang Playground has been deprecated and will be discontinued. New projects can Defang Playground is being deprecated. Users are encouraged to use the **Starter** tier (free) to deploy to their own cloud account via [BYOC](./defang-byoc). Existing Playground users will be migrated to the Starter tier. ::: -Defang aims to make it easier to deploy your services to the cloud. Specifically, Defang's goal is to make it easier to deploy your workloads to your *own* cloud accounts. We refer to this as bring-your-own-cloud (BYOC), which you can read about in more depth [here](./defang-byoc). +Defang aims to make it easier to deploy your services to the cloud. Specifically, Defang's goal is to make it easier to deploy your workloads to your _own_ cloud accounts. We refer to this as bring-your-own-cloud (BYOC), which you can read about in more depth [here](./defang-byoc). Defang Playground is a legacy free environment that allows you to deploy services to a Defang-hosted cloud account without needing to manage your own. It is intended for non-production workloads only. @@ -31,8 +31,8 @@ When you deploy a service to Defang Playground, it will be assigned a domain und ### Max Resources -* Projects: 1 -* Services: 4 -* CPUs: 2 -* Memory: 1GiB -* Replicas: 1 +- Projects: 1 +- Services: 4 +- CPUs: 2 +- Memory: 1GiB +- Replicas: 1 diff --git a/docs/concepts/deployment-modes.md b/docs/concepts/deployment-modes.md index 1ce8d28150..62822d6224 100644 --- a/docs/concepts/deployment-modes.md +++ b/docs/concepts/deployment-modes.md @@ -17,11 +17,11 @@ defang compose up --mode=high_availability ## Tier Requirements -| Mode | Required Plan | -|-|-| -| Affordable | Available on all tiers (Starter, Pro, Enterprise) | -| Balanced | Requires Pro ($49/mo) or Enterprise | -| High Availability | Requires Enterprise ($499/mo) | +| Mode | Required Plan | +| ----------------- | ------------------------------------------------- | +| Affordable | Available on all tiers (Starter, Pro, Enterprise) | +| Balanced | Requires Pro ($49/mo) or Enterprise | +| High Availability | Requires Enterprise ($499/mo) | :::tip While the `--mode` flag still works, the recommended way to choose a mode is to configure a [stack](/docs/concepts/stacks), which records the provider, region, and mode for a deployment so you don't have to pass these flags on every command. diff --git a/docs/concepts/deployments.md b/docs/concepts/deployments.md index 6506a8836c..4f5d61a6f6 100644 --- a/docs/concepts/deployments.md +++ b/docs/concepts/deployments.md @@ -21,7 +21,6 @@ By default, using the `affordable` mode, Defang will deprovision your existing s In [Defang BYOC](./defang-byoc.md), Defang uses your cloud provider account to build and store your images. ::: - ### Deployment Modes As mentioned above, Defang offers different [deployment modes](/docs/concepts/deployment-modes): `affordable`, `balanced`, and `high_availability`. You can switch the modes using the `--mode` CLI flag, though the recommended approach is to configure a [stack](/docs/concepts/stacks), which records the provider, region, and mode for a deployment. diff --git a/docs/concepts/estimation.md b/docs/concepts/estimation.md index bf1985394f..2b18c375f5 100644 --- a/docs/concepts/estimation.md +++ b/docs/concepts/estimation.md @@ -14,6 +14,7 @@ Currently, AWS and GCP are supported for cost estimation. ## Generating an Estimate Navigate your shell to your application's working directory and run + ``` defang estimate [--provider=aws|gcp] [--mode=affordable|balanced|high_availability] ``` @@ -65,10 +66,9 @@ defang compose up [--provider aws|azure|gcp|digitalocean] [--mode affordable|bal ## Supported Providers -| Provider | Estimation Support | -|----------------|:------------------:| -| AWS | ✅ | -| Azure | ❌ | -| DigitalOcean | ❌ | -| GCP | ✅ | - +| Provider | Estimation Support | +| ------------ | :----------------: | +| AWS | ✅ | +| Azure | ❌ | +| DigitalOcean | ❌ | +| GCP | ✅ | diff --git a/docs/concepts/local-development.md b/docs/concepts/local-development.md index 8df74fe082..4857354838 100644 --- a/docs/concepts/local-development.md +++ b/docs/concepts/local-development.md @@ -37,7 +37,7 @@ services: - .:/web local_service: image: myservice:latest - ``` +``` This configuration can then be launched locally with diff --git a/docs/concepts/managed-llms/managed-language-models.md b/docs/concepts/managed-llms/managed-language-models.md index fec0846175..1b7570f44b 100644 --- a/docs/concepts/managed-llms/managed-language-models.md +++ b/docs/concepts/managed-llms/managed-language-models.md @@ -13,12 +13,12 @@ Managed LLM services are available on all tiers (Starter, Pro, Enterprise) when ## Current Support -| Provider | Managed Language Models | -| --- | --- | -| [AWS Bedrock](/docs/providers/aws#managed-llms) | ✅ | -| [Microsoft Foundry](/docs/providers/azure) | ✅ | -| [DigitalOcean GenAI](/docs/providers/digitalocean#future-improvements) | ❌ | -| [GCP Vertex AI](/docs/providers/gcp#managed-llms) | ✅ | +| Provider | Managed Language Models | +| ---------------------------------------------------------------------- | ----------------------- | +| [AWS Bedrock](/docs/providers/aws#managed-llms) | ✅ | +| [Microsoft Foundry](/docs/providers/azure) | ✅ | +| [DigitalOcean GenAI](/docs/providers/digitalocean#future-improvements) | ❌ | +| [GCP Vertex AI](/docs/providers/gcp#managed-llms) | ✅ | ## Usage @@ -45,5 +45,5 @@ Assume you have a web service like the following, which uses the cloud native SD If you already have an OpenAI-compatible application, Defang makes it easy to deploy on your favourite cloud's managed LLM service. See our [OpenAI Access Gateway](/docs/concepts/managed-llms/openai-access-gateway). :::tip -Defang has a [*Managed LLM sample*](https://github.com/DefangLabs/samples/tree/main/samples/managed-llm) that uses the OpenAI Access Gateway, and a [*Managed LLM with Docker Model Provider sample*](https://github.com/DefangLabs/samples/tree/main/samples/managed-llm-provider) that uses a Docker Model Provider. +Defang has a [_Managed LLM sample_](https://github.com/DefangLabs/samples/tree/main/samples/managed-llm) that uses the OpenAI Access Gateway, and a [_Managed LLM with Docker Model Provider sample_](https://github.com/DefangLabs/samples/tree/main/samples/managed-llm-provider) that uses a Docker Model Provider. ::: diff --git a/docs/concepts/managed-llms/openai-access-gateway.md b/docs/concepts/managed-llms/openai-access-gateway.md index 1795612ffd..df3a5d90fb 100644 --- a/docs/concepts/managed-llms/openai-access-gateway.md +++ b/docs/concepts/managed-llms/openai-access-gateway.md @@ -39,13 +39,14 @@ The `x-defang-llm` extension is used to configure the appropriate roles and perm Defang supports model mapping through the [openai-access-gateway](https://github.com/DefangLabs/openai-access-gateway) on AWS and GCP. This takes a model with a [Docker naming convention](https://hub.docker.com/catalogs/models) (e.g. `ai/llama3.3`) and maps it to the closest matching model name on the target platform. If no such match can be found, it can fallback onto a known existing model (e.g. `ai/mistral`). This can be configured through the following environment variables: -* `USE_MODEL_MAPPING` (default to true) - configures whether or not model mapping should be enabled. -* `FALLBACK_MODEL` (no default) - configure a model which will be used if model mapping fails to find a target model. + +- `USE_MODEL_MAPPING` (default to true) - configures whether or not model mapping should be enabled. +- `FALLBACK_MODEL` (no default) - configure a model which will be used if model mapping fails to find a target model. ## Current Support -| Provider | Managed Language Models | -| --- | --- | -| [AWS Bedrock](/docs/providers/aws#managed-llms) | ✅ | -| [DigitalOcean GenAI](/docs/providers/digitalocean#future-improvements) | ❌ | -| [GCP Vertex AI](/docs/providers/gcp#managed-llms) | ✅ | +| Provider | Managed Language Models | +| ---------------------------------------------------------------------- | ----------------------- | +| [AWS Bedrock](/docs/providers/aws#managed-llms) | ✅ | +| [DigitalOcean GenAI](/docs/providers/digitalocean#future-improvements) | ❌ | +| [GCP Vertex AI](/docs/providers/gcp#managed-llms) | ✅ | diff --git a/docs/concepts/managed-storage/managed-mongodb.md b/docs/concepts/managed-storage/managed-mongodb.md index 2e2e8fe137..cd81f2ec86 100644 --- a/docs/concepts/managed-storage/managed-mongodb.md +++ b/docs/concepts/managed-storage/managed-mongodb.md @@ -13,12 +13,12 @@ Managed MongoDB is a service that allows you to store and retrieve large amounts ## Current Support -| Provider | Managed MongoDB | -| --- | --- | -| [AWS](/docs/providers/aws#managed-storage) | ✅ DocumentDB | -| [Azure](/docs/providers/azure) | ❌ | -| [DigitalOcean](/docs/providers/digitalocean#future-improvements) | ⚠️ Unmanaged | -| [GCP](/docs/providers/gcp#future-improvements) | ✅ Firestore | +| Provider | Managed MongoDB | +| ---------------------------------------------------------------- | --------------- | +| [AWS](/docs/providers/aws#managed-storage) | ✅ DocumentDB | +| [Azure](/docs/providers/azure) | ❌ | +| [DigitalOcean](/docs/providers/digitalocean#future-improvements) | ⚠️ Unmanaged | +| [GCP](/docs/providers/gcp#future-improvements) | ✅ Firestore | ## How to use Managed MongoDB @@ -29,6 +29,7 @@ Defang will use the image tag to determine the version to provision from your cl ### Required Configuration ### AWS + When using managed MongoDB on AWS, you **must** set a username and password for the database. By default, these are read from the `MONGO_INITDB_ROOT_USERNAME` and `MONGO_INITDB_ROOT_PASSWORD` config variables, following [the official MongoDB container image](https://hub.docker.com/_/mongo) convention. You can set these using the following commands: diff --git a/docs/concepts/managed-storage/managed-object-storage.md b/docs/concepts/managed-storage/managed-object-storage.md index 350a2675a4..89eaf30bdc 100644 --- a/docs/concepts/managed-storage/managed-object-storage.md +++ b/docs/concepts/managed-storage/managed-object-storage.md @@ -13,9 +13,9 @@ Managed Object Storage, like AWS S3, is a service that allows you to store and r ## Current Support -| Provider | Managed Object Storage | -| --- | --- | -| [AWS](/docs/providers/aws#managed-storage) | ❌ | -| [Azure](/docs/providers/azure) | ❌ | -| [DigitalOcean](/docs/providers/digitalocean#future-improvements) | ❌ | -| [GCP](/docs/providers/gcp#future-improvements) | ❌ | +| Provider | Managed Object Storage | +| ---------------------------------------------------------------- | ---------------------- | +| [AWS](/docs/providers/aws#managed-storage) | ❌ | +| [Azure](/docs/providers/azure) | ❌ | +| [DigitalOcean](/docs/providers/digitalocean#future-improvements) | ❌ | +| [GCP](/docs/providers/gcp#future-improvements) | ❌ | diff --git a/docs/concepts/managed-storage/managed-redis.md b/docs/concepts/managed-storage/managed-redis.md index 7d0fe5b6df..37efaae82c 100644 --- a/docs/concepts/managed-storage/managed-redis.md +++ b/docs/concepts/managed-storage/managed-redis.md @@ -9,12 +9,12 @@ Redis is an in-memory data structure store widely used for caching, real-time an ## Current Support -| Provider | Managed Redis | -| --- | --- | -| [AWS](/docs/providers/aws#managed-storage) | ✅ Elasticache | -| [Azure](/docs/providers/azure) | ✅ Azure Managed Redis| -| [DigitalOcean](/docs/providers/digitalocean#future-improvements) | ⚠️ Unmanaged | -| [GCP](/docs/providers/gcp#managed-redis) | ✅ Memorystore | +| Provider | Managed Redis | +| ---------------------------------------------------------------- | ---------------------- | +| [AWS](/docs/providers/aws#managed-storage) | ✅ Elasticache | +| [Azure](/docs/providers/azure) | ✅ Azure Managed Redis | +| [DigitalOcean](/docs/providers/digitalocean#future-improvements) | ⚠️ Unmanaged | +| [GCP](/docs/providers/gcp#managed-redis) | ✅ Memorystore | Managed Redis is available in all AWS and GCP regions. On Azure, availability varies by region — see [Managed Postgres and Redis region availability](/docs/providers/azure#managed-postgres-and-redis-region-availability) before choosing an `AZURE_LOCATION`. @@ -41,8 +41,6 @@ cache: When a project is deployed with the `production` [deployment mode](/docs/concepts/deployment-modes), any managed Redis instances are automatically configured to create a snapshot of the datastore before deletion. The snapshot will be named with the following format: -` ---redis--final-snapshot -` +`--redis--final-snapshot` The AWS Console can be used to restore a snapshot into a new instance of Redis. This feature is not yet supported on GCP. diff --git a/docs/concepts/one-off-jobs.md b/docs/concepts/one-off-jobs.md index 4ac511a44e..6405e0e57a 100644 --- a/docs/concepts/one-off-jobs.md +++ b/docs/concepts/one-off-jobs.md @@ -4,11 +4,13 @@ description: Defang enables you to run one-off jobs during your deployment workf --- # One-off Jobs + Defang enables you to run one-off jobs (a.k.a release tasks) during your deployment workflow. One-off jobs are commands that run at specific points in the deployment process, such as after your database is ready but before your application starts. One-off jobs are run a single time, and failure to run a one-off job will cause the entire deployment to fail. ## When should one-off jobs be used? + One-off jobs are useful for running commands that need to be executed before your application starts. Common use cases include: - Database migrations @@ -135,11 +137,9 @@ One off jobs are deployed as temporary containers on the same infrastructure as ### Supported Providers -| Provider | Release Task Support | -|----------------|:--------------------:| -| AWS | ✅ | -| Azure | ❌ | -| DigitalOcean | ❌ | -| GCP | ✅ | - - +| Provider | Release Task Support | +| ------------ | :------------------: | +| AWS | ✅ | +| Azure | ❌ | +| DigitalOcean | ❌ | +| GCP | ✅ | diff --git a/docs/concepts/portal.md b/docs/concepts/portal.md index 26fd6cfc2e..195a26935b 100644 --- a/docs/concepts/portal.md +++ b/docs/concepts/portal.md @@ -15,4 +15,4 @@ To view services deployed to Defang BYOC, please check out [Monitoring Your Serv :::tip Need help with a failing deployment? Defang provides a tool to help [debug](/docs/concepts/debug) in your application. -::: \ No newline at end of file +::: diff --git a/docs/concepts/projects.md b/docs/concepts/projects.md index bd51ea6fa7..01e96944cf 100644 --- a/docs/concepts/projects.md +++ b/docs/concepts/projects.md @@ -12,6 +12,7 @@ A _project_ refers to a cohesive collection of services which are defined and ma The _project name_ can be defined in the Compose file with the [`name` property](https://docs.docker.com/compose/compose-file/04-version-and-name/#name-top-level-element), otherwise the base name of the project directory will be used. The project name may then be used when performing project-wide operations such as listing services, tailing logs, or deprovisioning. For example: + ``` defang services --project-name defang tail --project-name diff --git a/docs/concepts/pulumi.md b/docs/concepts/pulumi.md index 02a96a4428..72def1bc3b 100644 --- a/docs/concepts/pulumi.md +++ b/docs/concepts/pulumi.md @@ -60,12 +60,14 @@ The following is a minimal example of a Pulumi program that defines a Defang ser import * as defang from "@defang-io/pulumi-defang/lib"; const service = new defang.DefangService("my-service", { - image: "strm/helloworld-http:latest", - ports: [{ - target: 80, - mode: "ingress", - protocol: "http", - }], + image: "strm/helloworld-http:latest", + ports: [ + { + target: 80, + mode: "ingress", + protocol: "http", + }, + ], }); ``` @@ -135,18 +137,25 @@ type Platform = "linux/arm64" | "linux/amd64" | "linux"; ``` ### `Protocol` + ```typescript type Protocol = "tcp" | "udp" | "http" | "http2" | "grpc"; ``` + ### `DeviceCapability` + ```typescript type DeviceCapability = "gpu"; ``` + ### `NetworkName` + ```typescript type NetworkName = "private" | "public"; ``` + ### `Network` + ```typescript type Network = { aliases?: string[] } | null; ``` diff --git a/docs/concepts/railpack.md b/docs/concepts/railpack.md index d845bf6a8d..2c80d48543 100644 --- a/docs/concepts/railpack.md +++ b/docs/concepts/railpack.md @@ -146,12 +146,12 @@ services: ## Supported Providers -| Provider | Railpack Support | -|----------------|:----------------:| -| AWS | ✅ | -| Azure | ❌ | -| DigitalOcean | ❌ | -| GCP | ✅ | +| Provider | Railpack Support | +| ------------ | :--------------: | +| AWS | ✅ | +| Azure | ❌ | +| DigitalOcean | ❌ | +| GCP | ✅ | ## Details diff --git a/docs/concepts/recipe.md b/docs/concepts/recipe.md index c67c6ce3e7..ff92c810d8 100644 --- a/docs/concepts/recipe.md +++ b/docs/concepts/recipe.md @@ -27,23 +27,23 @@ For example, on AWS, the built-in recipes set values like log retention (1 day f The three built-in recipes balance cost and resiliency: -* **Affordable**: Used for development and testing purposes. It typically involves less stringent resource allocations and may include debugging tools and verbose logging to aid in development. -* **Balanced**: Serves as a pre-production environment where applications are tested in conditions that closely mimic production. It helps in identifying issues that might not be apparent in the development environment. -* **High Availability**: Used for live deployments. It involves optimized configurations for performance, security, and reliability. Resource allocations are typically higher, and debugging tools are minimized to ensure stability. +- **Affordable**: Used for development and testing purposes. It typically involves less stringent resource allocations and may include debugging tools and verbose logging to aid in development. +- **Balanced**: Serves as a pre-production environment where applications are tested in conditions that closely mimic production. It helps in identifying issues that might not be apparent in the development environment. +- **High Availability**: Used for live deployments. It involves optimized configurations for performance, security, and reliability. Resource allocations are typically higher, and debugging tools are minimized to ensure stability. The following table compares what each built-in recipe configures: -| Feature | Affordable | Balanced | High Availability | -|-|-|-|-| -| Build Resources | Builds will be run with 2x vCPUs | (like `affordable`) | Builds will be run with 4x vCPUs | -| Compute | Using spot instances | (like `affordable`) | On-demand instances | -| Databases | Defang will provision resources optimized for burstable memory | (like `high_availability`) | Defang will provision resources optimized for production | -| Deployment | Previous deployments will be spun down before new deployments are spun up. Stopped tasks will not restart. | (like `high_availability`) | Rolling updates will be used to deploy new versions. Defang will gradually replace services while maintaining at least [the original number of replicas](/docs/tutorials/scaling-your-services). | -| Logs | Logs retained for 1 day to save costs. | Logs retained for 7 days to balance cost and access. | Logs retained for 30 days for compliance. | -| Networking | Only public IPs | Defang will provision a single NAT gateway | Defang will provision multiple NAT gateways. | -| Load Balancing | HTTP redirect to HTTPS using `302 Found` | | Termination Protection will be enabled; logs are retained on "down" | -| DNS | Defang will provision shorter TTLs; zones will be forcefully destroyed | | Defang will provision longer TTLs; records can be overwritten for ZDT | -| Managed Storage | Operations that cause downtime are allowed | | Encryption at rest; Final snapshot created on "down" | +| Feature | Affordable | Balanced | High Availability | +| --------------- | ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Build Resources | Builds will be run with 2x vCPUs | (like `affordable`) | Builds will be run with 4x vCPUs | +| Compute | Using spot instances | (like `affordable`) | On-demand instances | +| Databases | Defang will provision resources optimized for burstable memory | (like `high_availability`) | Defang will provision resources optimized for production | +| Deployment | Previous deployments will be spun down before new deployments are spun up. Stopped tasks will not restart. | (like `high_availability`) | Rolling updates will be used to deploy new versions. Defang will gradually replace services while maintaining at least [the original number of replicas](/docs/tutorials/scaling-your-services). | +| Logs | Logs retained for 1 day to save costs. | Logs retained for 7 days to balance cost and access. | Logs retained for 30 days for compliance. | +| Networking | Only public IPs | Defang will provision a single NAT gateway | Defang will provision multiple NAT gateways. | +| Load Balancing | HTTP redirect to HTTPS using `302 Found` | | Termination Protection will be enabled; logs are retained on "down" | +| DNS | Defang will provision shorter TTLs; zones will be forcefully destroyed | | Defang will provision longer TTLs; records can be overwritten for ZDT | +| Managed Storage | Operations that cause downtime are allowed | | Encryption at rest; Final snapshot created on "down" | ## Managing Recipes diff --git a/docs/concepts/scaling.md b/docs/concepts/scaling.md index 085a2f4a25..24f3563af4 100644 --- a/docs/concepts/scaling.md +++ b/docs/concepts/scaling.md @@ -39,7 +39,7 @@ With Defang, users on the Pro plan ($49/mo) or higher can enable service-level a 1. Add the _**x-defang-autoscaling : true**_ extension to the service you want to autoscale. 2. Remove any _**replicas**_ field in the _**deploy**_ mapping (if present). -3. Deploy using staging or production [mode](/docs/concepts/deployments#deployment-modes). (e.g. ```defang compose up --provider=aws --mode=production```) +3. Deploy using staging or production [mode](/docs/concepts/deployments#deployment-modes). (e.g. `defang compose up --provider=aws --mode=production`) ```yaml services: @@ -66,12 +66,12 @@ Auto-scaling systems typically rely on: ### Supported Providers -| Provider | Auto-Scaling Support | -|----------------|:--------------------:| -| AWS | ✅ | -| Azure | ❌ | -| DigitalOcean | ❌ | -| GCP | ✅ | +| Provider | Auto-Scaling Support | +| ------------ | :------------------: | +| AWS | ✅ | +| Azure | ❌ | +| DigitalOcean | ❌ | +| GCP | ✅ | ### Benefits of Auto-Scaling diff --git a/docs/concepts/services.md b/docs/concepts/services.md index 9c21054fbf..8206841980 100644 --- a/docs/concepts/services.md +++ b/docs/concepts/services.md @@ -14,7 +14,6 @@ Defang allows you to deploy services defined as containers. You can define your Defang identifies services by using your [account username](/docs/concepts/accounts), [project name](/docs/concepts/projects), and service name. The port is included in the [domain](/docs/concepts/domains) for the service. - :::tip Service names are defined in your Compose file or in your Pulumi program. ::: @@ -34,8 +33,8 @@ Service names are defined in your Compose file or in your Pulumi program. - ### Service Deployment + Defang manages the deployment process for services. You can learn more about how services are deployed in the [Deployment page](./deployments.md). :::info @@ -46,20 +45,20 @@ We plan to add support for other types of services in the future, including serv An overview of the possible statuses for a service in Defang. -| Status | Details | -|-|-| -| BUILD_QUEUED | The service update has been received and is now in the queue for its image to be built. | -| BUILD_PROVISIONING | The container orchestrator is provisioning the necessary resources for building your service's image. | -| BUILD_PENDING | The necessary resources to build your service have been provisioned but the build has not yet been initiated. | -| BUILD_ACTIVATING | The container orchestrator is pulling the build container's image and creating the build container. | -| BUILD_RUNNING | The container which builds your service's image is successfully running. | -| BUILD_STOPPING | The container orchestrator has sent a termination signal to the build container and is waiting for the build process to stop. | -| BUILD_FAILED | The build container exited with a non-zero status code. | -| UPDATE_QUEUED | The service update has been received and is now in the queue. | -| DEPLOYMENT_PENDING | The necessary resources to run your service have been provisioned but the service has not yet been initiated. | -| DEPLOYMENT_COMPLETED | Your service has been deployed and is healthy. | -| DEPLOYMENT_FAILED | Your service could not be deployed. | -| DEPLOYMENT_SCALED_IN | The deployment has been scaled-in (spun down). | +| Status | Details | +| -------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| BUILD_QUEUED | The service update has been received and is now in the queue for its image to be built. | +| BUILD_PROVISIONING | The container orchestrator is provisioning the necessary resources for building your service's image. | +| BUILD_PENDING | The necessary resources to build your service have been provisioned but the build has not yet been initiated. | +| BUILD_ACTIVATING | The container orchestrator is pulling the build container's image and creating the build container. | +| BUILD_RUNNING | The container which builds your service's image is successfully running. | +| BUILD_STOPPING | The container orchestrator has sent a termination signal to the build container and is waiting for the build process to stop. | +| BUILD_FAILED | The build container exited with a non-zero status code. | +| UPDATE_QUEUED | The service update has been received and is now in the queue. | +| DEPLOYMENT_PENDING | The necessary resources to run your service have been provisioned but the service has not yet been initiated. | +| DEPLOYMENT_COMPLETED | Your service has been deployed and is healthy. | +| DEPLOYMENT_FAILED | Your service could not be deployed. | +| DEPLOYMENT_SCALED_IN | The deployment has been scaled-in (spun down). | :::tip You can run the `defang compose ps` command to view the status of your services. diff --git a/docs/concepts/stacks.md b/docs/concepts/stacks.md index 686aad6f91..674502d39e 100644 --- a/docs/concepts/stacks.md +++ b/docs/concepts/stacks.md @@ -3,7 +3,6 @@ title: Stacks description: Defang supports deploying multiple instances of a project as separate stacks. --- - # Stacks Defang supports deploying multiple instances of a project as separate _stacks_. Each stack represents an isolated deployment of your project. Stacks allow you to manage different deployments of your project. For example, you can use stacks to support distinct development, staging, and production environments in the same cloud account. You can also use stacks to support deployments for different customers (e.g., Client A, Client B) or regions (e.g., North America, Europe) within the same project. @@ -56,7 +55,7 @@ This command shows all stacks in your project. defang stack list NAME PROVIDER REGION MODE -production aws us-west-2 BALANCED +production aws us-west-2 BALANCED ``` ## Internals diff --git a/docs/concepts/workspaces.md b/docs/concepts/workspaces.md index f6f22c6670..999741048f 100644 --- a/docs/concepts/workspaces.md +++ b/docs/concepts/workspaces.md @@ -103,6 +103,7 @@ For workspaces created in the Defang Portal: 4. The owner approves or denies the request from the portal Members can have different roles: + - **Owner**: Full control including billing, settings, and member management - **Admin**: Can manage members and settings - **Member**: Can use the workspace subscription for deployments diff --git a/docs/intro/faq/_category_.json b/docs/intro/faq/_category_.json index dd0a2f94d6..c962c39aac 100644 --- a/docs/intro/faq/_category_.json +++ b/docs/intro/faq/_category_.json @@ -1,9 +1,8 @@ { - "label": "FAQ", - "position": 600, - "link": { - "type": "generated-index", - "description": "Wondering about something? Check out one of the guides below for additional support." - } + "label": "FAQ", + "position": 600, + "link": { + "type": "generated-index", + "description": "Wondering about something? Check out one of the guides below for additional support." } - \ No newline at end of file +} diff --git a/docs/intro/faq/questions.md b/docs/intro/faq/questions.md index 3c9ef40089..5898609f10 100644 --- a/docs/intro/faq/questions.md +++ b/docs/intro/faq/questions.md @@ -3,6 +3,7 @@ sidebar_position: 600 title: Frequently Asked Questions description: Frequently asked questions about Defang Deploy. --- + import {Button, ButtonGroup, FormGroup, FormLabel} from "@mui/material" # Frequently Asked Questions (FAQ) @@ -76,16 +77,19 @@ import {Button, ButtonGroup, FormGroup, FormLabel} from "@mui/material" ### Is Defang Deploy a run-time platform? + - No. Defang Deploy is not a run-time platform. Instead, it lets you host and run your application on a [cloud provider](/docs/providers) of your choice. You can think of it as a tool that makes it way easier to deploy to that cloud provider. ### What is the difference between Defang Deploy and platforms such as Vercel, fly.io, Railway, Render, or Heroku? + - Defang Deploy is a tool that helps you get your application deployed to a [cloud provider](/docs/providers) of your choice, and it is not a platform. Unlike platforms, Defang Deploy does not host your application. ### What is the difference between Defang Deploy and tools such as SST? + - Defang Deploy is cloud-agnostic and language-agnostic, meaning that it is designed to work with different [cloud providers](/docs/providers), and programming languages. Since Defang Deploy is not tied to just one cloud or language, this allows for greater flexibility in a wide range of cases. Another difference is that Defang Deploy follows the [Compose specification](https://docs.docker.com/compose/compose-file/), allowing it to work smoothly with various container platforms such as Docker. ## Troubleshooting @@ -93,10 +97,10 @@ import {Button, ButtonGroup, FormGroup, FormLabel} from "@mui/material" ### I'm having trouble running the binary on my Mac. What should I do? - MacOS users will need to allow the binary to run due to security settings: - 1. Attempt to run the binary. You'll see a security prompt preventing you from running it. - 2. Go to System Preferences > Privacy & Security > General. - 3. In the 'Allow applications downloaded from:' section, you should see a message about Defang being blocked. Click 'Open Anyway'. - 4. Alternatively, select the option "App Store and identified developers" to allow all applications from the App Store and identified developers to run. + 1. Attempt to run the binary. You'll see a security prompt preventing you from running it. + 2. Go to System Preferences > Privacy & Security > General. + 3. In the 'Allow applications downloaded from:' section, you should see a message about Defang being blocked. Click 'Open Anyway'. + 4. Alternatively, select the option "App Store and identified developers" to allow all applications from the App Store and identified developers to run. ### I'm getting a warning/error. What does it mean? diff --git a/docs/intro/faq/warnings-errors.md b/docs/intro/faq/warnings-errors.md index b1a64984af..59e842df90 100644 --- a/docs/intro/faq/warnings-errors.md +++ b/docs/intro/faq/warnings-errors.md @@ -9,19 +9,25 @@ Here are the meanings of common [warning](#warnings) and [error](#errors) messag ## Warnings ### "The folder is not empty. Files may be overwritten." + - This message is displayed when you run `defang generate` and the target folder is not empty. If you proceed, Defang will overwrite any existing files with the same name. If you want to keep the existing files, you should move them to a different folder before running `defang generate` or pick a different target folder. ### "environment variable not found" + - This message is displayed when you run `defang compose up` and the Compose file references an environment variable that is not set. If you proceed, the environment variable will be empty in the container. If you want to set the environment variable, you should set it in the environment where you run `defang compose up`. ### "Unsupported platform" + - This message is displayed when you run `defang compose up` and the Compose file references a platform that is not supported by Defang. ### "not logged in" + - This message is displayed when you run `defang compose config` but you are not logged in. The displayed configuration will be incomplete. If you want to see the complete configuration, you should log in first using `defang login`. ### "No port mode was specified; assuming 'host'" + - This message is displayed when you run `defang compose up` and the Compose file declares a `port` that does not specify a port `mode`. By default, Defang will keep the port private. If you want to expose the port to the public internet, you should specify the `mode` as `ingress`: + ``` services: service1: @@ -32,16 +38,21 @@ services: ``` ### "Published ports are not supported in ingress mode; assuming 'host'" + - This message is displayed when you run `defang compose up` and the Compose file declares a `port` with `mode` set to `ingress` and `published` set to a port number. Defang does not support published ports in ingress mode. If you want to expose the port to the public internet, you should specify the `mode` as `ingress` and remove the `published` setting. ### "TCP ingress is not supported; assuming HTTP" + - This message is displayed when you run `defang compose up` and the Compose file declares a `port` with `mode` set to `ingress` and `protocol` set to `tcp`. Defang does not support arbitrary TCP ingress and will assume the port is used for HTTP traffic. To silence the warning, remove the `protocol` setting. ### "unsupported compose directive" + - This message is displayed when you run `defang compose up` and the Compose file declares a directive that is not supported by Defang. The deployment will continue, but the unsupported directive will be ignored, which may cause unexpected behavior. ### "no reservations specified; using limits as reservations" + - This message is displayed when you run `defang compose up` and the Compose file declares a `resource` with `limits` but no `reservations`. Defang will use the `limits` as `reservations` to ensure the container has enough resources. Specify `reservations` if you want to silence the warning or reserve a different amount of resources: + ``` services: service1: @@ -54,7 +65,9 @@ services: ``` ### "ingress port without healthcheck defaults to GET / HTTP/1.1" + - This message is displayed when you run `defang compose up` and the Compose file declares an `ingress` with a `port` but no `healthcheck`. Defang will assume the default healthcheck of `GET / HTTP/1.1` to ensure the port is healthy. Specify a `healthcheck` if you want to silence the warning or use a different healthcheck: + ``` services: service1: @@ -65,7 +78,9 @@ services: ``` ### "missing memory reservation; specify deploy.resources.reservations.memory to avoid out-of-memory errors" + - This message is displayed when you run `defang compose up` and the Compose file doesn't specify a `memory` reservation. If available, Defang will use the `memory` limit as the `memory` reservation. Specify a `memory` reservation if you want to silence the warning or reserve a different amount of memory: + ``` services: service1: @@ -77,17 +92,21 @@ services: ``` ### "The build context contains more than 10 files" + - This message is displayed when you run `defang compose up` and the Compose file declares a `build` with a `context` that contains more than 10 files. Ensure the context refers to the correct folder. Defang will use the `context` as is, but you may experience slow build times. If you want to speed up the build, you should reduce the number of files in the `context`. ### "AWS provider was selected, but AWS environment variables are not set" + - This message is displayed when you run `defang compose up` with the `--provider=aws` but none of the AWS environment variables were set. If you proceed, the deployment might fail, unless you have defined defined `default` credentials in the AWS configuration files or are running on an AWS instance. ### "Using Defang provider, but AWS environment variables were detected" + - This message is displayed when you run `defang compose up` with the `--provider=defang` but AWS environment variables were detected. The AWS environment variables will be ignored. ## Errors ### "Stack:… is in UPDATE_COMPLETE_CLEANUP_IN_PROGRESS state and cannot be updated" + - This happens if different version of the Defang CLI are used with the same AWS account. Each version one will try to update the CD stack to its version, back and forth. Make sure that all users have the same version of the CLI. Check the CLI version using `defang version`. ### "invalid healthcheck: ingress ports require an HTTP healthcheck on `localhost`." @@ -96,27 +115,28 @@ services: 1. Should my service be public? It's common to declare your container's ports using the Compose file "shorthand" syntax (`1234:1234`). This syntax can be understood as `[HOST:]CONTAINER`. If your service is not intended to be public, you do not need to declare a HOST port. For example: - ```diff - services: - my-service: - image: my-image - ports: - - - "1234:1234" - + - "1234" - ``` + ```diff + services: + my-service: + image: my-image + ports: + - - "1234:1234" + + - "1234" + ``` + 2. Does my healthcheck include the string `localhost`? It is very common to define a healthcheck by using `curl` or `wget` to make a request to `localhost`. So common, in fact, that Defang will look for the string `localhost` in your healthcheck definition. For example, this healthcheck is valid: - ```yaml - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:1234/health"] - ``` + ```yaml + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:1234/health"] + ``` - This healthcheck is not valid for `ingress` ports: + This healthcheck is not valid for `ingress` ports: - ```yaml - healthcheck: - test: ["CMD", "./my-healthcheck"] - ``` + ```yaml + healthcheck: + test: ["CMD", "./my-healthcheck"] + ``` ### "The build aborted with OutOfMemoryError: Container killed due to memory usage" diff --git a/docs/providers/aws.md b/docs/providers/aws.md index accfe0e360..3e36f52aab 100644 --- a/docs/providers/aws.md +++ b/docs/providers/aws.md @@ -14,7 +14,6 @@ You can use the AWS Free Tier to try out Defang. Learn more about it [here](http ## Getting Started - If you don't yet have an AWS account, check out our guide on (setting up an AWS account for the first time](/docs/tutorials/setting_up_your_aws_account) If you already have an AWS account set up, getting started with the Defang BYOC AWS Provider is easy. The first step is to [authenticate your shell](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html) with AWS as an admin user. The authenticated user should be an IAM admin because Defang will need permission to create resources and IAM roles in your account. @@ -97,51 +96,51 @@ Defang will provision a DocumentDB instance for services that use the `x-defang- Defang will create and manage the following resources in your AWS account from its bootstrap CloudFormation template: -| Resource Type | Example Resource Name | -|---------------|------------------------| -| s3/Bucket | defang-cd-bucket-cbpbzz8hzm7 | -| ecs/ClusterCapacityProviderAssociations | defang-cd-Cluster-pqFhjwuklvm | -| ecs/Cluster | defang-cd-ClusterpJqFhjwuklvm | -| iam/Role | defang-cd-ExeutionRole-XE7RbQDfeEwx | -| ec2/InternetGateway | igw-05bd7adc92541ec3 | -| ec2/VPCGatewayAttachment | IGW|vpc-0cbca64f13435695 | -| logs/LogGroup | defang-cd-Logroup-6LSZet3tFnEy | -| ecr/PullThroughCacheRule | defang-cd-ecrpublic | -| ec2/Route | rtb-08f3f5afc9e6c8c8|0.0.0.0/0 | -| ec2/RouteTable | rtb-08f3f5ffc9e6c8c8 | -| ec2/VPCEndpoint | vpce-02175d8d4f47d0c9 | -| ec2/SecurityGroup | sg-032b839c63e70e49 | -| ec2/Subnet | subnet-086bead399ddc8a0 | -| ec2/SubnetRouteTableAssociation | rtbassoc-02e200d45e7227fe | -| ecs/TaskDefinition | arn:aws:ecsus-west-2:381492210770:task-definition/defang-cd-TaskDefinition-RXd5tf9TaN38:1 | -| iam/Role | defang-cd-askRole-gsEeDPd6sPQY | -| ec2/VPC | vpc-0cbca64f13435695 | +| Resource Type | Example Resource Name | +| --------------------------------------- | ----------------------------------------------------------------------------------------- | +| s3/Bucket | defang-cd-bucket-cbpbzz8hzm7 | +| ecs/ClusterCapacityProviderAssociations | defang-cd-Cluster-pqFhjwuklvm | +| ecs/Cluster | defang-cd-ClusterpJqFhjwuklvm | +| iam/Role | defang-cd-ExeutionRole-XE7RbQDfeEwx | +| ec2/InternetGateway | igw-05bd7adc92541ec3 | +| ec2/VPCGatewayAttachment | IGW | vpc-0cbca64f13435695 | +| logs/LogGroup | defang-cd-Logroup-6LSZet3tFnEy | +| ecr/PullThroughCacheRule | defang-cd-ecrpublic | +| ec2/Route | rtb-08f3f5afc9e6c8c8 | 0.0.0.0/0 | +| ec2/RouteTable | rtb-08f3f5ffc9e6c8c8 | +| ec2/VPCEndpoint | vpce-02175d8d4f47d0c9 | +| ec2/SecurityGroup | sg-032b839c63e70e49 | +| ec2/Subnet | subnet-086bead399ddc8a0 | +| ec2/SubnetRouteTableAssociation | rtbassoc-02e200d45e7227fe | +| ecs/TaskDefinition | arn:aws:ecsus-west-2:381492210770:task-definition/defang-cd-TaskDefinition-RXd5tf9TaN38:1 | +| iam/Role | defang-cd-askRole-gsEeDPd6sPQY | +| ec2/VPC | vpc-0cbca64f13435695 | Then, for each project you deploy, Defang will create and manage the following resources: -| Resource Type | Example Resource Name | -|---------------|------------------------| -| ecr/Repository | project1/kaniko-build | -| ecr/LifecyclePolicy | project1/kaniko-build | -| acm/Certificate | *.project1.tenant1.defang.app | -| ecr/Repository | project1/kaniko-build/cache | -| ecr/LifecyclePolicy | project1/kaniko-build/cache | -| iam/InstanceProfile | ecs-agent-profile | -| iam/Role | ecs-task-execution-role | -| cloudwatch/EventRule | project1-ecs-lifecycle-rule | -| cloudwatch/EventTarget | project1-ecs-event-cw-target | -| route53/Record | validation-project1.tenant1.defang.app | -| acm/CertificateValidation | *.project1.tenant1.defang.appValidation | -| ec2/VpcDhcpOptionsAssociation | dhcp-options-association | -| cloudwatch/LogGroup | builds | -| iam/Role | kaniko-task-role | -| ecs/TaskDefinition | kanikoTaskDefArm64 | -| ecs/TaskDefinition | kanikoTaskDefAmd64 | -| s3/Bucket | defang-build | -| s3/BucketPublicAccessBlock | defang-build-block | -| ecs/Cluster | cluster | -| ecs/ClusterCapacityProviders | cluster-capacity-providers | -| ec2/SecurityGroup | project1_app-sg | -| ec2/SecurityGroup | bootstrap | -| ec2/VpcDhcpOptions | dhcp-options | -| cloudwatch/LogGroup | logs | +| Resource Type | Example Resource Name | +| ----------------------------- | --------------------------------------- | +| ecr/Repository | project1/kaniko-build | +| ecr/LifecyclePolicy | project1/kaniko-build | +| acm/Certificate | *.project1.tenant1.defang.app | +| ecr/Repository | project1/kaniko-build/cache | +| ecr/LifecyclePolicy | project1/kaniko-build/cache | +| iam/InstanceProfile | ecs-agent-profile | +| iam/Role | ecs-task-execution-role | +| cloudwatch/EventRule | project1-ecs-lifecycle-rule | +| cloudwatch/EventTarget | project1-ecs-event-cw-target | +| route53/Record | validation-project1.tenant1.defang.app | +| acm/CertificateValidation | *.project1.tenant1.defang.appValidation | +| ec2/VpcDhcpOptionsAssociation | dhcp-options-association | +| cloudwatch/LogGroup | builds | +| iam/Role | kaniko-task-role | +| ecs/TaskDefinition | kanikoTaskDefArm64 | +| ecs/TaskDefinition | kanikoTaskDefAmd64 | +| s3/Bucket | defang-build | +| s3/BucketPublicAccessBlock | defang-build-block | +| ecs/Cluster | cluster | +| ecs/ClusterCapacityProviders | cluster-capacity-providers | +| ec2/SecurityGroup | project1_app-sg | +| ec2/SecurityGroup | bootstrap | +| ec2/VpcDhcpOptions | dhcp-options | +| cloudwatch/LogGroup | logs | diff --git a/docs/providers/gcp.md b/docs/providers/gcp.md index 12b9a27eca..cc0f784bdf 100644 --- a/docs/providers/gcp.md +++ b/docs/providers/gcp.md @@ -82,6 +82,7 @@ Defang offers integration with managed, cloud-native large language model servic ### Future Improvements The following features are in active development for GCP: + - [Managed Object Storage](/docs/concepts//managed-storage/managed-object-storage.md) Stayed tuned for future updates! diff --git a/docs/tutorials/configure-environment-variables.md b/docs/tutorials/configure-environment-variables.md index d81bb9871d..9d97d3fd9f 100644 --- a/docs/tutorials/configure-environment-variables.md +++ b/docs/tutorials/configure-environment-variables.md @@ -5,15 +5,16 @@ description: How to configure sensitive environment variables in Defang. # Configure Environment Variables - This tutorial will show you how to configure sensitive environment variables in Defang. ## Pre-requisites -* [A `compose.yaml` file in your project](https://docs.docker.com/compose/gettingstarted/) -* [A Defang Account](/docs/concepts/authentication) -* [The Defang CLI](/docs/intro/getting-started#install-the-defang-cli) + +- [A `compose.yaml` file in your project](https://docs.docker.com/compose/gettingstarted/) +- [A Defang Account](/docs/concepts/authentication) +- [The Defang CLI](/docs/intro/getting-started#install-the-defang-cli) ## Step 1 - Go to your `compose.yaml` file + :::info If you are using [Pulumi](/docs/concepts/pulumi) instead of Compose files to define your services, please see [Using Config With Pulumi](/docs/concepts/configuration#using-config-with-pulumi) instead. ::: @@ -30,7 +31,8 @@ services: - API_KEY ``` -The type of notation shown above is called *list notation*. Alternatively, you can use *map notation*, which is also acceptable: +The type of notation shown above is called _list notation_. Alternatively, you can use _map notation_, which is also acceptable: + ```yaml services: service1: @@ -40,10 +42,13 @@ services: ``` ## Step 2 - Set the actual value in the Defang CLI + To store the actual (sensitive) value of the variable, open up a terminal and type the command: + ```bash defang config set API_KEY=actualvalue ``` + Remember to replace `API_KEY` with your variable name and `actualvalue` with your actual value. :::tip @@ -51,24 +56,31 @@ You can view all the config variables you are storing in Defang by doing: `defan ::: ### Editing a config value + To edit a value, you can run the command again with an updated value to overwrite the current value: + ```bash defang config set API_KEY=newvalue ``` ### Removing a config value + To remove a value, run the command: + ```bash defang config rm API_KEY ``` + :::tip Remember to update your Compose file if you remove an environment variable. ::: ## Step 3 - Deploy + ```bash defang compose up ``` --- + For a deeper discussion on how configuration works in Defang, see our [Configuration docs](/docs/concepts/configuration). diff --git a/docs/tutorials/deploying-from-github-actions.md b/docs/tutorials/deploying-from-github-actions.md index 9f993bc61d..e19fef7e58 100644 --- a/docs/tutorials/deploying-from-github-actions.md +++ b/docs/tutorials/deploying-from-github-actions.md @@ -8,6 +8,7 @@ description: Using the Defang Github Action to deploy your project from your CI/ Defang makes it easy to deploy your applications directly from your GitHub Actions workflow using the [Defang GitHub Action](https://github.com/DefangLabs/defang-github-action). There is a dedicated tutorial for deploying to each cloud provider: -* [AWS](/docs/tutorials/deploying-from-github-actions/to-aws) -* [Azure](/docs/tutorials/deploying-from-github-actions/to-azure) -* [GCP](/docs/tutorials/deploying-from-github-actions/to-gcp). + +- [AWS](/docs/tutorials/deploying-from-github-actions/to-aws) +- [Azure](/docs/tutorials/deploying-from-github-actions/to-azure) +- [GCP](/docs/tutorials/deploying-from-github-actions/to-gcp). diff --git a/docs/tutorials/estimating-aws-deployment-costs.md b/docs/tutorials/estimating-aws-deployment-costs.md index d0f8afc8fb..ec281504da 100644 --- a/docs/tutorials/estimating-aws-deployment-costs.md +++ b/docs/tutorials/estimating-aws-deployment-costs.md @@ -35,11 +35,18 @@ services: target: 5432 published: 5432 healthcheck: - test: ["CMD", "python3", "-c", "import sys, urllib.request; urllib.request.urlopen(sys.argv[1]).read()", "http://localhost:8000/"] + test: + [ + "CMD", + "python3", + "-c", + "import sys, urllib.request; urllib.request.urlopen(sys.argv[1]).read()", + "http://localhost:8000/", + ] deploy: resources: reservations: - cpus: '0.5' + cpus: "0.5" memory: 256M django: @@ -62,7 +69,7 @@ services: deploy: resources: reservations: - cpus: '0.5' + cpus: "0.5" memory: 256M ``` @@ -120,4 +127,3 @@ Now that you have estimated the costs associated with your project. You are read ``` defang compose up --provider aws --mode affordable|balanced|high_availability ``` - diff --git a/docs/tutorials/estimating-gcp-deployment-costs.md b/docs/tutorials/estimating-gcp-deployment-costs.md index 1834e47426..b034e43daa 100644 --- a/docs/tutorials/estimating-gcp-deployment-costs.md +++ b/docs/tutorials/estimating-gcp-deployment-costs.md @@ -35,11 +35,18 @@ services: target: 5432 published: 5432 healthcheck: - test: ["CMD", "python3", "-c", "import sys, urllib.request; urllib.request.urlopen(sys.argv[1]).read()", "http://localhost:8000/"] + test: + [ + "CMD", + "python3", + "-c", + "import sys, urllib.request; urllib.request.urlopen(sys.argv[1]).read()", + "http://localhost:8000/", + ] deploy: resources: reservations: - cpus: '0.5' + cpus: "0.5" memory: 256M django: @@ -62,7 +69,7 @@ services: deploy: resources: reservations: - cpus: '0.5' + cpus: "0.5" memory: 256M ``` @@ -122,4 +129,3 @@ Now that you have estimated the costs associated with your project. You are read ``` defang compose up --provider gcp --mode affordable|balanced|high_availability ``` - diff --git a/docs/tutorials/migrating-from-heroku.md b/docs/tutorials/migrating-from-heroku.md index 4fd4250313..3a6201ef57 100644 --- a/docs/tutorials/migrating-from-heroku.md +++ b/docs/tutorials/migrating-from-heroku.md @@ -7,13 +7,14 @@ description: Deploy your Heroku Applications in your own Cloud Account with Defa This tutorial will guide you through the process of migrating your Heroku applications to your own cloud account with Defang. This will allow you to reduce cost, gain more control, and access to the complete range of available cloud services. ## Pre-requisites -* [A Defang Account](/docs/concepts/authentication) -* [The Defang CLI](/docs/intro/getting-started#install-the-defang-cli) -* [The Heroku CLI (optional, but recommended)](https://devcenter.heroku.com/articles/heroku-cli#install-the-heroku-cli) -* Cloud Account Credentials - * [AWS](/docs/tutorials/setting_up_your_aws_account) - * [Azure](https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli) - * [GCP](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment) + +- [A Defang Account](/docs/concepts/authentication) +- [The Defang CLI](/docs/intro/getting-started#install-the-defang-cli) +- [The Heroku CLI (optional, but recommended)](https://devcenter.heroku.com/articles/heroku-cli#install-the-heroku-cli) +- Cloud Account Credentials + - [AWS](/docs/tutorials/setting_up_your_aws_account) + - [Azure](https://learn.microsoft.com/en-us/cli/azure/authenticate-azure-cli) + - [GCP](https://cloud.google.com/docs/authentication/set-up-adc-local-dev-environment) :::tip **Do I need a Dockerfile?** @@ -37,6 +38,7 @@ Gemfile.lock README.md app config db log storage tmp ``` Then run `defang init` and select `Migrate from heroku`: + ``` $ defang init ? How would you like to start? [Use arrows to move, type to filter] @@ -85,6 +87,7 @@ To deploy this project, run At this point, `defang` will have generated a `compose.yaml` file that describes your application and its services. You can review this file in your favorite text editor. The application I used had a single `web` dyno: + ``` $ heroku ps -a vast-badlands-production @@ -92,6 +95,7 @@ $ heroku ps -a vast-badlands-production ``` And a single PostgreSQL database: + ``` heroku addons -a vast-badlands-production @@ -138,7 +142,7 @@ services: deploy: resources: limits: - cpus: '1' + cpus: "1" memory: 512M release: @@ -156,7 +160,7 @@ services: deploy: resources: limits: - cpus: '1' + cpus: "1" memory: 512M restart: "no" ``` @@ -279,6 +283,7 @@ AWS_REGION=us-west-2 AWS_PROFILE=default defang compose up --provider aws 2025-08-28T14:53:34.230-07:00 web * Listening on http://0.0.0.0:5000 2025-08-28T14:53:34.232-07:00 web Use Ctrl-C to stop ``` + See our full tutorial on [deploying to AWS](/docs/tutorials/deploy-to-aws). @@ -365,7 +370,6 @@ export SPACES_SECRET_ACCESS_KEY=your_spaces_secret_access_key $ defang compose up --provider digitalocean ``` - ## Step 3 - Migrating your data :::tip diff --git a/docs/tutorials/monitoring-your-services.md b/docs/tutorials/monitoring-your-services.md index c90ae0e224..c812f7e8a6 100644 --- a/docs/tutorials/monitoring-your-services.md +++ b/docs/tutorials/monitoring-your-services.md @@ -42,5 +42,6 @@ $ defang logs --type=build All of the above flags can be combined to get the exact logs you need. See the CLI reference for [`defang tail`](/docs/cli/defang_tail) for more information. :::info -* To learn more about observability in Defang, check out the [Observability page](../concepts/observability.md). -::: \ No newline at end of file + +- To learn more about observability in Defang, check out the [Observability page](../concepts/observability.md). + ::: diff --git a/docs/tutorials/setting_up_your_gcp_account.md b/docs/tutorials/setting_up_your_gcp_account.md index 2c82ecba48..a62de92f8f 100644 --- a/docs/tutorials/setting_up_your_gcp_account.md +++ b/docs/tutorials/setting_up_your_gcp_account.md @@ -5,7 +5,7 @@ description: Follow these steps to set up your Google Cloud Platform (GCP) accou # Setting Up Your GCP Account - Follow these steps to set up your Google Cloud Platform (GCP) account for deploying applications with Defang. +Follow these steps to set up your Google Cloud Platform (GCP) account for deploying applications with Defang. --- @@ -30,6 +30,7 @@ To create a new project: 1. Visit the [GCP Console](https://console.cloud.google.com/). 2. Click the project selector button (it may say **"Select a project"** or display a previous project name). +
GCP console with the project select button highlighted
Select project button to open the project dialog
diff --git a/docs/tutorials/updating-your-services.md b/docs/tutorials/updating-your-services.md index 29104690f9..7fb623ccf8 100644 --- a/docs/tutorials/updating-your-services.md +++ b/docs/tutorials/updating-your-services.md @@ -41,10 +41,10 @@ To delete your app, use `defang compose down` in your compose file working direc In some cases, particularly on the AWS platform, additional actions may be required. Specifically load balancers may have Deletion Protection on. To turn this off in the AWS Console for EC2 Load Balancers, follow these steps: - 1. Select the load balancer corresponding to the app’s name. - 2. Go to the Attributes tab. - 3. Click the Edit button. - 4. Locate Deletion Protection and disable it. + 1. Select the load balancer corresponding to the app’s name. + 2. Go to the Attributes tab. + 3. Click the Edit button. + 4. Locate Deletion Protection and disable it. :::info For more information on Deployment Modes, see the [Deployment Modes](/docs/concepts/deployment-modes) concept documentation. diff --git a/docs/tutorials/using-codespaces-gitpod.md b/docs/tutorials/using-codespaces-gitpod.md index e592ff76c0..95f85612c2 100644 --- a/docs/tutorials/using-codespaces-gitpod.md +++ b/docs/tutorials/using-codespaces-gitpod.md @@ -11,60 +11,62 @@ This tutorial will guide you to set up Defang in both GitHub Codespaces and Gitp ## Using Codespaces With Defang ### Step 1 - Clone the Defang Codespace Project -Start by cloning the [Defang GitHub-Codespace](https://github.com/DefangLabs/github-codespace) repo and pushing it to your own account. This repository is configured with a Codespace that has Defang pre-installed. +Start by cloning the [Defang GitHub-Codespace](https://github.com/DefangLabs/github-codespace) repo and pushing it to your own account. This repository is configured with a Codespace that has Defang pre-installed. ### Step 2 - Create a Codespace + Once you've pushed to your own GitHub repo, you'll be able to create a Codespace by clicking the Code button, selecting the Codespaces tab, and clicking the + icon. This will set up a development environment with Defang already installed, which might take a few minutes. ![Create Codespace button screenshot](/img/codespace-tutorial/new-codespace.png) - ### Step 3 - Open in VS Code Desktop -For the `defang login` command to work correctly, you must open the Codespace in VS Code desktop. This is required because the login process is designed to run on localhost. +For the `defang login` command to work correctly, you must open the Codespace in VS Code desktop. This is required because the login process is designed to run on localhost. ![Open in vs code desktop button screenshot](/img/codespace-tutorial/desktop.png) - ### Step 4 - Run Defang Login + Within a VS Code desktop terminal, execute the following command. + ```bash defang login ``` -Although it may initially refuse to connect on your localhost, going back will show a "successfully logged in" message, confirming that you're logged into Defang. +Although it may initially refuse to connect on your localhost, going back will show a "successfully logged in" message, confirming that you're logged into Defang. ### Step 5 - Verify Running Services -Now that you're logged in, you can use Defang commands. You can test that everything is working properly by running `defang ls` to list your running services. +Now that you're logged in, you can use Defang commands. You can test that everything is working properly by running `defang ls` to list your running services. ## Using Gitpod With Defang ### Step 1 - Clone the Defang Gitpod Workspace Project -Start by cloning the [Defang Gitpod-Workspace](https://github.com/DefangLabs/gitpod-workspace) repo and pushing it to your own GitHub, GitLab, or BitBucket account. This repository includes a Workspace configuration that pre-installs Defang. +Start by cloning the [Defang Gitpod-Workspace](https://github.com/DefangLabs/gitpod-workspace) repo and pushing it to your own GitHub, GitLab, or BitBucket account. This repository includes a Workspace configuration that pre-installs Defang. ### Step 2 - Initialize a Gitpod Workspace + Navigate `https://gitpod.io/#` to create your new workspace. In the repository, we have a YAML file indicating that we are using a pre-built Dockerfile which installs Defang CLI for you. - ### Step 3 - Lauch VS Code from Gitpod + Open VS Code from Gitpod, you will likely need to have the Gitpod VS Code extension installed. ![Open in vs code desktop button screenshot](/img/codespace-tutorial/gitpod-desktop.png) ![Screenshot of Gitpod extension](/img/codespace-tutorial/gitpod-ext.png) - ### Step 4 - Run Defang Login + Within a VS Code desktop terminal, execute the following command. ```bash defang login ``` - ### Step 5 - Verify Running Services + Now that you're logged in, you can use Defang commands. You can test that everything is working properly by running `defang ls` to list your running services. diff --git a/docs/tutorials/using-one-click-deploy.md b/docs/tutorials/using-one-click-deploy.md index 915efaefa9..c19df815a2 100644 --- a/docs/tutorials/using-one-click-deploy.md +++ b/docs/tutorials/using-one-click-deploy.md @@ -14,6 +14,7 @@ To access the full range of features provided by Defang, we recommend using the ::: ## Step 1 - Choose a Sample + Head to our [list of samples](https://defang.io/#samples) and click a sample you want to deploy. Then, click on the button that says "1-Click Deploy". one-click-deploy-button @@ -32,7 +33,6 @@ For 1-click deployments to work, Defang must have your permission, which you can ![login-screen](/img/use-one-click-tutorial/login-screen.png) - ## Step 3 - Create Your Repo Once logged in, you'll be redirected to GitHub. Click the "Create repository button" to create a new repository with the sample project. @@ -40,12 +40,11 @@ Once logged in, you'll be redirected to GitHub. Click the "Create repository but create-repository
- ## Step 4 - Wait for Deployment to Complete A Github Action workflow will automatically start running to install Defang and deploy the sample to the Defang Playground. You can see this by going into the "Actions" tab in your GitHub repository. -You can view the status of your deployment in the [Defang Portal](https://portal.defang.dev/), or by downloading the [Defang CLI](/docs/intro/getting-started). You can also see deployment progress in the "Actions" tab of your GitHub repository: +You can view the status of your deployment in the [Defang Portal](https://portal.defang.dev/), or by downloading the [Defang CLI](/docs/intro/getting-started). You can also see deployment progress in the "Actions" tab of your GitHub repository: github-actions-tab @@ -57,6 +56,7 @@ If you decide to make a commit later to a repository created from 1-Click Deploy ::: When it is completed, you can view your deployed app using the deployment link generated by Defang, which should appear similar to the format below: + ``` https://---.defang.dev ``` diff --git a/package-lock.json b/package-lock.json index a1cf125525..309a54a9fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,7 +40,10 @@ "@docusaurus/types": "^3.9.1", "@types/react": "^18.2.29", "autoprefixer": "^10.4.21", + "husky": "^9.1.7", + "lint-staged": "^17.0.4", "postcss": "^8.5.26", + "prettier": "^3.8.3", "tailwindcss": "^3.4.13", "tailwindcss-animate": "^1.0.7", "typescript": "~5.2.2" @@ -7636,6 +7639,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-table3": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz", @@ -7668,6 +7687,69 @@ "node": ">=8" } }, + "node_modules/cli-truncate": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-5.2.0.tgz", + "integrity": "sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "slice-ansi": "^8.0.0", + "string-width": "^8.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/cli-truncate/node_modules/string-width": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, "node_modules/clone-deep": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", @@ -9589,6 +9671,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -10428,6 +10523,19 @@ "node": ">=6.9.0" } }, + "node_modules/get-east-asian-width": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", + "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -11659,6 +11767,22 @@ "node": ">=10.17.0" } }, + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", + "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, "node_modules/hyperdyperid": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", @@ -12390,6 +12514,145 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/lint-staged": { + "version": "17.0.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.0.4.tgz", + "integrity": "sha512-+rU9lSUyVOZ/hDUmRLVGzyS2v73cDdQjX+XQz1AaOdIE4RysLq0HoPW2HrrgeNCLklkhi904VBU1bmgWLHVnkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "listr2": "^10.2.1", + "picomatch": "^4.0.4", + "string-argv": "^0.3.2", + "tinyexec": "^1.1.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": ">=22.22.1" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + }, + "optionalDependencies": { + "yaml": "^2.8.4" + } + }, + "node_modules/lint-staged/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/listr2": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-10.2.1.tgz", + "integrity": "sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cli-truncate": "^5.2.0", + "eventemitter3": "^5.0.4", + "log-update": "^6.1.0", + "rfdc": "^1.4.1", + "wrap-ansi": "^10.0.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/listr2/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/listr2/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/listr2/node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/listr2/node_modules/string-width": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.1.tgz", + "integrity": "sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/listr2/node_modules/wrap-ansi": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-10.0.0.tgz", + "integrity": "sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "string-width": "^8.2.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/loader-runner": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", @@ -12473,6 +12736,160 @@ "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==" }, + "node_modules/log-update": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz", + "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "cli-cursor": "^5.0.0", + "slice-ansi": "^7.1.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/log-update/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-update/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/slice-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", + "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "is-fullwidth-code-point": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-update/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -14809,6 +15226,19 @@ "node": ">=6" } }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mimic-response": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", @@ -17192,6 +17622,22 @@ "postcss": "^8.4.31" } }, + "node_modules/prettier": { + "version": "3.9.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.8.tgz", + "integrity": "sha512-WRFq3Wn3WId7LLROfMLdH7xaFr2jR62wU8nLO6rQUOLOxNZUviyJQs1M0iIhLexSFy+L+w0ch66wtoO2jRjG0A==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-error": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", @@ -18152,6 +18598,52 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", @@ -18169,6 +18661,13 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "dev": true, + "license": "MIT" + }, "node_modules/robust-predicates": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", @@ -18756,6 +19255,52 @@ "node": ">=8" } }, + "node_modules/slice-ansi": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", + "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.3", + "is-fullwidth-code-point": "^5.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", + "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.3.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/snake-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", @@ -18898,6 +19443,16 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -19371,9 +19926,13 @@ "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, "node_modules/tinyexec": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.1.tgz", - "integrity": "sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/tinypool": { "version": "1.1.1", @@ -20769,9 +21328,9 @@ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/package.json b/package.json index 9b941f7d18..959c42083f 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,10 @@ "serve": "docusaurus serve", "write-translations": "docusaurus write-translations", "write-heading-ids": "docusaurus write-heading-ids", - "typecheck": "tsc" + "typecheck": "tsc", + "format": "prettier --write .", + "format:check": "prettier --check .", + "prepare": "husky" }, "dependencies": { "@docsearch/docusaurus-adapter": "^4.6.3", @@ -49,7 +52,10 @@ "@docusaurus/types": "^3.9.1", "@types/react": "^18.2.29", "autoprefixer": "^10.4.21", + "husky": "^9.1.7", + "lint-staged": "^17.0.4", "postcss": "^8.5.26", + "prettier": "^3.8.3", "tailwindcss": "^3.4.13", "tailwindcss-animate": "^1.0.7", "typescript": "~5.2.2" @@ -68,5 +74,8 @@ }, "engines": { "node": ">=18.0" + }, + "lint-staged": { + "*.{ts,tsx,js,jsx,md,json,css}": "prettier --write" } } diff --git a/postcss.config.js b/postcss.config.js index 33ad091d26..12a703d900 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -3,4 +3,4 @@ module.exports = { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/scripts/prep-cli-docs.js b/scripts/prep-cli-docs.js index 9ec2fd5913..ac5a2a764e 100644 --- a/scripts/prep-cli-docs.js +++ b/scripts/prep-cli-docs.js @@ -1,8 +1,8 @@ -const fs = require('fs'); -const path = require('path'); -const matter = require('gray-matter'); +const fs = require("fs"); +const path = require("path"); +const matter = require("gray-matter"); -const directoryPath = path.join(__dirname, '../docs/cli'); +const directoryPath = path.join(__dirname, "../docs/cli"); // Rename files // fs.readdirSync(directoryPath).forEach(file => { @@ -14,37 +14,40 @@ const directoryPath = path.join(__dirname, '../docs/cli'); // }); // Add frontmatter and update links -fs.readdirSync(directoryPath).forEach(file => { - if (path.extname(file) === '.md') { - const filePath = path.join(directoryPath, file); - let fileContent = fs.readFileSync(filePath, 'utf-8'); - const frontMatter = matter(fileContent); +fs.readdirSync(directoryPath).forEach((file) => { + if (path.extname(file) === ".md") { + const filePath = path.join(directoryPath, file); + let fileContent = fs.readFileSync(filePath, "utf-8"); + const frontMatter = matter(fileContent); - const title = path.basename(file, '.md').replace(/_/g, ' '); + const title = path.basename(file, ".md").replace(/_/g, " "); - // replace the first instance of the title - fileContent = fileContent.replace(`## ${title}\n\n`, ''); + // replace the first instance of the title + fileContent = fileContent.replace(`## ${title}\n\n`, ""); - if (!frontMatter.data.title) { - fileContent = `---\ntitle: ${title}\n---\n\n${fileContent}`; - } + if (!frontMatter.data.title) { + fileContent = `---\ntitle: ${title}\n---\n\n${fileContent}`; + } - // Replace links - // fileContent = fileContent.replace(/\(([^)]*\.md)\)/g, (match, p1) => { - // return `(${p1.replace(/_/g, '-')})`; - // }); + // Replace links + // fileContent = fileContent.replace(/\(([^)]*\.md)\)/g, (match, p1) => { + // return `(${p1.replace(/_/g, '-')})`; + // }); - const angleBracketRegex = /<\(([^)]*)\)/g; + const angleBracketRegex = /<\(([^)]*)\)/g; - console.log('Current filename: ', file); - console.log('Potential markdown rendering issues: ', fileContent.match(angleBracketRegex)); + console.log("Current filename: ", file); + console.log( + "Potential markdown rendering issues: ", + fileContent.match(angleBracketRegex), + ); - // Replace <(arg) with \<(arg) to avoid markdown rendering - fileContent = fileContent.replace(angleBracketRegex, '\\<($1)'); + // Replace <(arg) with \<(arg) to avoid markdown rendering + fileContent = fileContent.replace(angleBracketRegex, "\\<($1)"); - const curlyBracketRegex = /{([^}]*)}/g; - fileContent = fileContent.replace(curlyBracketRegex, '\\{$1\\}'); + const curlyBracketRegex = /{([^}]*)}/g; + fileContent = fileContent.replace(curlyBracketRegex, "\\{$1\\}"); - fs.writeFileSync(filePath, fileContent); - } + fs.writeFileSync(filePath, fileContent); + } }); diff --git a/scripts/prep-samples.js b/scripts/prep-samples.js index 0847ea017a..8df12f23d4 100644 --- a/scripts/prep-samples.js +++ b/scripts/prep-samples.js @@ -1,9 +1,9 @@ -const fs = require('fs'); -const path = require('path'); -const YAML = require('yaml'); +const fs = require("fs"); +const path = require("path"); +const YAML = require("yaml"); const samplesDir = process.argv[2]; -let samplesMdContent = '' +let samplesMdContent = ""; // Inspired by https://github.com/compose-spec/compose-go/blob/main/template/template.go const interpolationRegex = @@ -13,123 +13,144 @@ const interpolationRegex = // listed as configs a developer has to set. Mirror of the CLI's // reservedConfigNames (DefangLabs/defang: src/pkg/cli/compose/validation.go). const RESERVED_CONFIG_VARS = new Set([ - 'COMPOSE_PROJECT_NAME', - 'DEFANG_PROVIDER', - 'DEFANG_STACK', + "COMPOSE_PROJECT_NAME", + "DEFANG_PROVIDER", + "DEFANG_STACK", ]); function interpolatedVars(str) { - return Array.from(str.matchAll(interpolationRegex), (match) => match[1] || match[2]); + return Array.from( + str.matchAll(interpolationRegex), + (match) => match[1] || match[2], + ); } // categories are directories in the current directory (i.e. we're running in samples/ and we might have a samples/ruby/ directory) -const directories = fs.readdirSync(samplesDir).filter(file => fs.statSync(path.join(samplesDir, file)).isDirectory()); +const directories = fs + .readdirSync(samplesDir) + .filter((file) => fs.statSync(path.join(samplesDir, file)).isDirectory()); let jsonArray = []; directories.forEach((sample) => { - const directoryName = sample; - console.log(`@@ Adding ${sample}`); - let readme; - try { - readme = fs.readFileSync(path.join(samplesDir, sample, 'README.md'), 'utf8'); - // replace the text after the first `#` with a link to the sample - // readme = readme.replace(/^# (.*)/, `# [$1](https://github.com/DefangLabs/samples/tree/main/samples/${sample})`); - readme = readme.replace(/^# (.*)/, (match, p1) => { - return `# [${p1}](https://github.com/DefangLabs/samples/tree/main/samples/${sample})`; + const directoryName = sample; + console.log(`@@ Adding ${sample}`); + let readme; + try { + readme = fs.readFileSync( + path.join(samplesDir, sample, "README.md"), + "utf8", + ); + // replace the text after the first `#` with a link to the sample + // readme = readme.replace(/^# (.*)/, `# [$1](https://github.com/DefangLabs/samples/tree/main/samples/${sample})`); + readme = readme.replace(/^# (.*)/, (match, p1) => { + return `# [${p1}](https://github.com/DefangLabs/samples/tree/main/samples/${sample})`; + }); + samplesMdContent += readme + "\n\n"; + } catch (error) { + readme = `# ${sample}`; + } + + // The readme should contain lines that start with the following: + // Title: + // Short Description: + // Tags: + // Languages: + // + // We want to extract the title, short description, tags, and languages from the readme. Tags and languages are comma separated lists. + let title, shortDescription, tags, languages; + try { + title = readme.match(/Title: (.*)/)[1]; + shortDescription = readme.match(/Short Description: (.*)/)[1]; + tags = readme + .match(/Tags: (.*)/)[1] + .split(",") + .map((tag) => tag.trim()); + languages = readme + .match(/Languages: (.*)/)[1] + .split(",") + .map((language) => language.trim()); + } catch (error) { + console.log(`@@ Failed to parse readme for sample ${sample}`, error); + return; + } + + let configs = new Set(); + try { + composeFile = fs.readFileSync( + path.join(samplesDir, sample, "compose.yaml"), + "utf8", + ); + compose = YAML.parse(composeFile); + + for (var name in compose.services) { + service = compose.services[name]; + if (Array.isArray(service.environment)) { + service.environment.forEach((env) => { + if (!env.includes("=")) { + configs.add(env); + } + interpolatedVars(env).forEach((v) => { + configs.add(v); + }); }); - samplesMdContent += readme + "\n\n"; - } catch (error) { - readme = `# ${sample}`; - } - - // The readme should contain lines that start with the following: - // Title: - // Short Description: - // Tags: - // Languages: - // - // We want to extract the title, short description, tags, and languages from the readme. Tags and languages are comma separated lists. - let title, shortDescription, tags, languages; - try { - title = readme.match(/Title: (.*)/)[1]; - shortDescription = readme.match(/Short Description: (.*)/)[1]; - tags = readme.match(/Tags: (.*)/)[1].split(',').map(tag => tag.trim()); - languages = readme.match(/Languages: (.*)/)[1].split(',').map(language => language.trim()); - } catch (error) { - console.log(`@@ Failed to parse readme for sample ${sample}`, error); - return; - } - - let configs = new Set(); - try { - composeFile = fs.readFileSync(path.join(samplesDir, sample, 'compose.yaml'), 'utf8'); - compose = YAML.parse(composeFile); - - for (var name in compose.services) { - service = compose.services[name] - if (Array.isArray(service.environment)) { - service.environment.forEach(env => { - if (!env.includes("=")) { - configs.add(env); - } - interpolatedVars(env).forEach(v => { - configs.add(v); - }); - }); - } else { - for (var name in service.environment) { - value = service.environment[name]; - if (value === null || value === undefined || value === "") { - configs.add(name); - } - if (typeof value === 'string') { - interpolatedVars(value).forEach(v => { - configs.add(v); - }); - } - } - } - } - } catch (error) { - // Ignore if the sample doesn't have a compose file - if (error.code != 'ENOENT') { - console.log(`failed to parse compose for configs for sample`, sample, error); + } else { + for (var name in service.environment) { + value = service.environment[name]; + if (value === null || value === undefined || value === "") { + configs.add(name); + } + if (typeof value === "string") { + interpolatedVars(value).forEach((v) => { + configs.add(v); + }); + } } + } } - - RESERVED_CONFIG_VARS.forEach((v) => configs.delete(v)); - - const sampleSummary = { - name: directoryName, - category: languages?.[0], - readme, - directoryName, - title, - shortDescription, - tags, - languages, - }; - if (configs.size > 0) { - sampleSummary.configs = Array.from(configs); + } catch (error) { + // Ignore if the sample doesn't have a compose file + if (error.code != "ENOENT") { + console.log( + `failed to parse compose for configs for sample`, + sample, + error, + ); } - jsonArray.push(sampleSummary); - - console.log(`@@ Added ${sample}`); + } + + RESERVED_CONFIG_VARS.forEach((v) => configs.delete(v)); + + const sampleSummary = { + name: directoryName, + category: languages?.[0], + readme, + directoryName, + title, + shortDescription, + tags, + languages, + }; + if (configs.size > 0) { + sampleSummary.configs = Array.from(configs); + } + jsonArray.push(sampleSummary); + + console.log(`@@ Added ${sample}`); }); const stringified = JSON.stringify(jsonArray, null, 2); // exclude any lines which start with '---' -samplesMdContent = samplesMdContent.replace(/---.*\n/g, ''); +samplesMdContent = samplesMdContent.replace(/---.*\n/g, ""); // exclude any lines which include markdown images `![` -samplesMdContent = samplesMdContent.replace(/^.*!\[.*\]\(.*\).*\n?/gm, ''); +samplesMdContent = samplesMdContent.replace(/^.*!\[.*\]\(.*\).*\n?/gm, ""); // increase the header level of all headers in the markdown content samplesMdContent = samplesMdContent.replace(/^(#{1,6})\s/gm, (match) => { - const headerLevel = match.length - 1; // number of '#' characters - const newHeaderLevel = Math.min(headerLevel + 1, 6); - return '#'.repeat(newHeaderLevel) + ' '; + const headerLevel = match.length - 1; // number of '#' characters + const newHeaderLevel = Math.min(headerLevel + 1, 6); + return "#".repeat(newHeaderLevel) + " "; }); const frontMatter = `--- @@ -141,9 +162,12 @@ description: A collection of sample applications and configurations for Defang L // prefix samplesMdContent with the front matter samplesMdContent = frontMatter + "\n\n" + samplesMdContent; -const samplesMd = path.join(__dirname, '..', 'docs', 'samples.md'); +const samplesMd = path.join(__dirname, "..", "docs", "samples.md"); console.log(`@@ Writing samples markdown to ${samplesMd}`); fs.writeFileSync(samplesMd, samplesMdContent); // save the json to the samples.json file in static -fs.writeFileSync(path.join(__dirname, '..', 'static', 'samples-v2.json'), stringified); +fs.writeFileSync( + path.join(__dirname, "..", "static", "samples-v2.json"), + stringified, +); diff --git a/sidebars.js b/sidebars.js index 9eb02e6248..5315c945c7 100644 --- a/sidebars.js +++ b/sidebars.js @@ -14,28 +14,18 @@ /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */ const sidebars = { mainSidebar: [ - { type: 'autogenerated', dirName: 'intro' }, + { type: "autogenerated", dirName: "intro" }, { type: "link", label: "Samples", href: "https://defang.io/samples/", - } - ], - conceptsSidebar: [ - { type: 'autogenerated', dirName: 'concepts' }, - ], - tutorialsSidebar: [ - { type: 'autogenerated', dirName: 'tutorials' }, - ], - providersSidebar: [ - { type: 'autogenerated', dirName: 'providers' }, - ], - cliSidebar: [ - { type: 'autogenerated', dirName: 'cli' }, - ], - stationSidebar: [ - { type: 'autogenerated', dirName: 'station' }, + }, ], + conceptsSidebar: [{ type: "autogenerated", dirName: "concepts" }], + tutorialsSidebar: [{ type: "autogenerated", dirName: "tutorials" }], + providersSidebar: [{ type: "autogenerated", dirName: "providers" }], + cliSidebar: [{ type: "autogenerated", dirName: "cli" }], + stationSidebar: [{ type: "autogenerated", dirName: "station" }], }; module.exports = sidebars; diff --git a/src/components/HomepageFeatures/index.tsx b/src/components/HomepageFeatures/index.tsx index 91ef4601d2..ee86e91956 100644 --- a/src/components/HomepageFeatures/index.tsx +++ b/src/components/HomepageFeatures/index.tsx @@ -1,17 +1,17 @@ -import React from 'react'; -import clsx from 'clsx'; -import styles from './styles.module.css'; +import React from "react"; +import clsx from "clsx"; +import styles from "./styles.module.css"; type FeatureItem = { title: string; - Svg: React.ComponentType>; + Svg: React.ComponentType>; description: JSX.Element; }; const FeatureList: FeatureItem[] = [ { - title: 'Easy to Use', - Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default, + title: "Easy to Use", + Svg: require("@site/static/img/undraw_docusaurus_mountain.svg").default, description: ( <> Docusaurus was designed from the ground up to be easily installed and @@ -20,8 +20,8 @@ const FeatureList: FeatureItem[] = [ ), }, { - title: 'Focus on What Matters', - Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default, + title: "Focus on What Matters", + Svg: require("@site/static/img/undraw_docusaurus_tree.svg").default, description: ( <> Docusaurus lets you focus on your docs, and we'll do the chores. Go @@ -30,8 +30,8 @@ const FeatureList: FeatureItem[] = [ ), }, { - title: 'Powered by React', - Svg: require('@site/static/img/undraw_docusaurus_react.svg').default, + title: "Powered by React", + Svg: require("@site/static/img/undraw_docusaurus_react.svg").default, description: ( <> Extend or customize your website layout by reusing React. Docusaurus can @@ -41,9 +41,9 @@ const FeatureList: FeatureItem[] = [ }, ]; -function Feature({title, Svg, description}: FeatureItem) { +function Feature({ title, Svg, description }: FeatureItem) { return ( -
+
diff --git a/src/components/IntroGrid.tsx b/src/components/IntroGrid.tsx index f1e97bb94e..6bc566ea1c 100644 --- a/src/components/IntroGrid.tsx +++ b/src/components/IntroGrid.tsx @@ -1,14 +1,8 @@ - import DocCardList from "@docusaurus/theme-classic/lib/theme/DocCardList"; -import type {PropSidebarItem} from '@docusaurus/plugin-content-docs'; - -const items: PropSidebarItem[] = [ - -] +import type { PropSidebarItem } from "@docusaurus/plugin-content-docs"; -export default function() { +const items: PropSidebarItem[] = []; - return ( - - ); +export default function () { + return ; } diff --git a/src/components/OneClick/index.tsx b/src/components/OneClick/index.tsx index 8fd11f7465..ee03e5e091 100644 --- a/src/components/OneClick/index.tsx +++ b/src/components/OneClick/index.tsx @@ -1,12 +1,10 @@ -import { Stack, TextField } from '@mui/material'; -import { createContext, useContext, useState } from 'react'; -import CodeBlock from '@theme/CodeBlock'; - - +import { Stack, TextField } from "@mui/material"; +import { createContext, useContext, useState } from "react"; +import CodeBlock from "@theme/CodeBlock"; const URLContext = createContext({ url: "", - setUrl: (url: string) => { } + setUrl: (url: string) => {}, }); export function URLProvider({ children }: { children: React.ReactNode }) { @@ -18,13 +16,16 @@ export function URLProvider({ children }: { children: React.ReactNode }) { ); } - - export function OriginalRepoUrl() { const { url, setUrl } = useContext(URLContext); return ( - setUrl(e.target.value)} label="Your Repo URL" helperText="Like https://github.com/defanglabs/defang" /> + setUrl(e.target.value)} + label="Your Repo URL" + helperText="Like https://github.com/defanglabs/defang" + /> ); } @@ -57,7 +58,6 @@ function getEncodedTemplateUrl(url: string) { return encodeURIComponent(templateUrl); } - export function EncodedTemplateUrl() { const { url } = useContext(URLContext); const encodedTemplateUrl = getEncodedTemplateUrl(url); @@ -73,7 +73,6 @@ function getOneClickUrl(url: string) { return `https://portal.defang.dev/redirect?url=${encodedTemplateUrl}`; } - export function OneClickUrl() { const { url } = useContext(URLContext); const oneClickUrl = getOneClickUrl(url); @@ -88,10 +87,12 @@ export function URLEncode() { const [val, setVal] = useState(""); return ( - setVal(e.target.value)} /> - - {encodeURIComponent(val)} - + setVal(e.target.value)} + /> + {encodeURIComponent(val)} - ) + ); } diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx index b9ad2376f1..8fc476cb5c 100644 --- a/src/components/ui/badge.tsx +++ b/src/components/ui/badge.tsx @@ -1,25 +1,27 @@ -import * as React from 'react'; -import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from "react"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from '@/lib/utils'; +import { cn } from "@/lib/utils"; const badgeVariants = cva( - 'inline-flex items-center rounded-full border px-3 py-1 text-xs font-semibold uppercase tracking-wide transition-colors', + "inline-flex items-center rounded-full border px-3 py-1 text-xs font-semibold uppercase tracking-wide transition-colors", { variants: { variant: { - default: 'border-transparent bg-secondary text-secondary-foreground shadow-sm', - outline: 'text-muted-foreground border-border/80' - } + default: + "border-transparent bg-secondary text-secondary-foreground shadow-sm", + outline: "text-muted-foreground border-border/80", + }, }, defaultVariants: { - variant: 'default' - } - } + variant: "default", + }, + }, ); export interface BadgeProps - extends React.HTMLAttributes, + extends + React.HTMLAttributes, VariantProps {} export function Badge({ className, variant, ...props }: BadgeProps) { diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 6b67fe995e..f8a1d23b2c 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -1,43 +1,46 @@ -import * as React from 'react'; -import { Slot } from '@radix-ui/react-slot'; -import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from '@/lib/utils'; +import { cn } from "@/lib/utils"; const buttonVariants = cva( - 'inline-flex items-center justify-center whitespace-nowrap rounded-full text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-background', + "inline-flex items-center justify-center whitespace-nowrap rounded-full text-sm font-medium transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 ring-offset-background", { variants: { variant: { - default: 'bg-gradient-to-r from-primary/95 to-primary text-primary-foreground shadow-glow hover:translate-y-0.5 hover:shadow-lg', - subtle: 'bg-secondary text-secondary-foreground hover:bg-secondary/80', - outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground', - ghost: 'hover:bg-accent hover:text-accent-foreground', - link: 'text-primary underline-offset-4 hover:underline' + default: + "bg-gradient-to-r from-primary/95 to-primary text-primary-foreground shadow-glow hover:translate-y-0.5 hover:shadow-lg", + subtle: "bg-secondary text-secondary-foreground hover:bg-secondary/80", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", }, size: { - default: 'h-11 px-6 py-2', - sm: 'h-9 rounded-full px-4', - lg: 'h-12 rounded-full px-8 text-base', - icon: 'h-11 w-11' - } + default: "h-11 px-6 py-2", + sm: "h-9 rounded-full px-4", + lg: "h-12 rounded-full px-8 text-base", + icon: "h-11 w-11", + }, }, defaultVariants: { - variant: 'default', - size: 'default' - } - } + variant: "default", + size: "default", + }, + }, ); export interface ButtonProps - extends React.ButtonHTMLAttributes, + extends + React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; } const Button = React.forwardRef( ({ className, variant, size, asChild = false, ...props }, ref) => { - const Comp = asChild ? Slot : 'button'; + const Comp = asChild ? Slot : "button"; return ( ( {...props} /> ); - } + }, ); -Button.displayName = 'Button'; +Button.displayName = "Button"; export { Button, buttonVariants }; diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index a1e337bd75..a98801fadb 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -1,17 +1,21 @@ -import * as React from 'react'; +import * as React from "react"; -import { cn } from '@/lib/utils'; +import { cn } from "@/lib/utils"; -const Card = React.forwardRef>( - ({ className, ...props }, ref) => ( -
- ) -); -Card.displayName = 'Card'; +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Card.displayName = "Card"; const CardHeader = React.forwardRef< HTMLDivElement, @@ -19,11 +23,11 @@ const CardHeader = React.forwardRef< >(({ className, ...props }, ref) => (
)); -CardHeader.displayName = 'CardHeader'; +CardHeader.displayName = "CardHeader"; const CardTitle = React.forwardRef< HTMLParagraphElement, @@ -31,11 +35,14 @@ const CardTitle = React.forwardRef< >(({ className, ...props }, ref) => (

)); -CardTitle.displayName = 'CardTitle'; +CardTitle.displayName = "CardTitle"; const CardDescription = React.forwardRef< HTMLParagraphElement, @@ -43,11 +50,11 @@ const CardDescription = React.forwardRef< >(({ className, ...props }, ref) => (

)); -CardDescription.displayName = 'CardDescription'; +CardDescription.displayName = "CardDescription"; const CardContent = React.forwardRef< HTMLDivElement, @@ -55,10 +62,10 @@ const CardContent = React.forwardRef< >(({ className, ...props }, ref) => (

)); -CardContent.displayName = 'CardContent'; +CardContent.displayName = "CardContent"; export { Card, CardHeader, CardTitle, CardDescription, CardContent }; diff --git a/src/css/custom.css b/src/css/custom.css index ece185c06d..5f4165f092 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -1,21 +1,26 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap'); +@import url("https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap"); @tailwind base; @tailwind components; @tailwind utilities; @font-face { - font-family: 'GuarujaNeue-SemiBold'; - src: url('/fonts/guaruja-neue/GuarujaNeue-SemiBold.woff2') format('woff2'), - url('/fonts/guaruja-neue/GuarujaNeue-SemiBold.woff') format('woff'); + font-family: "GuarujaNeue-SemiBold"; + src: + url("/fonts/guaruja-neue/GuarujaNeue-SemiBold.woff2") format("woff2"), + url("/fonts/guaruja-neue/GuarujaNeue-SemiBold.woff") format("woff"); font-weight: 600; font-style: normal; } :root { color-scheme: light; - --font-sans: 'Inter', 'Inter var', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; - --font-display: 'GuarujaNeue-SemiBold', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; + --font-sans: + "Inter", "Inter var", -apple-system, BlinkMacSystemFont, "Segoe UI", + system-ui, sans-serif; + --font-display: + "GuarujaNeue-SemiBold", "Inter", -apple-system, BlinkMacSystemFont, + "Segoe UI", system-ui, sans-serif; --background: 216 65% 99%; --foreground: 222 76% 6%; --muted: 215 40% 93%; @@ -45,7 +50,9 @@ --ifm-color-primary-lighter: hsl(212, 92%, 66%); --ifm-color-primary-lightest: hsl(212, 92%, 72%); --ifm-font-family-base: var(--font-sans); - --ifm-font-family-monospace: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + --ifm-font-family-monospace: + "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; --ifm-heading-font-family: var(--font-display); --ifm-heading-font-weight: 600; --ifm-navbar-shadow: none; @@ -66,7 +73,7 @@ --ifm-footer-title-color: hsl(var(--foreground)); } -[data-theme='dark'] { +[data-theme="dark"] { color-scheme: dark; --background: 222 62% 7%; --foreground: 220 19% 92%; @@ -96,7 +103,6 @@ } @layer base { - *, *::before, *::after { @@ -111,19 +117,37 @@ #__docusaurus { @apply bg-background text-foreground antialiased; font-family: var(--font-sans); - background-image: radial-gradient(circle at top left, hsla(var(--primary) / 0.08), transparent 55%), radial-gradient(circle at 75% 10%, hsla(var(--secondary) / 0.12), transparent 45%); + background-image: + radial-gradient( + circle at top left, + hsla(var(--primary) / 0.08), + transparent 55% + ), + radial-gradient( + circle at 75% 10%, + hsla(var(--secondary) / 0.12), + transparent 45% + ); background-attachment: fixed; } - [data-theme='dark'] #__docusaurus { + [data-theme="dark"] #__docusaurus { @apply bg-background text-foreground antialiased; font-family: var(--font-sans); - background-image: radial-gradient(circle at 20% 15%, hsla(var(--primary) / 0.12), transparent 45%), - radial-gradient(circle at 80% 5%, hsla(var(--secondary) / 0.08), transparent 42%); + background-image: + radial-gradient( + circle at 20% 15%, + hsla(var(--primary) / 0.12), + transparent 45% + ), + radial-gradient( + circle at 80% 5%, + hsla(var(--secondary) / 0.08), + transparent 42% + ); background-attachment: fixed; } - h1, h2, h3, @@ -155,12 +179,20 @@ .navbar__item-get_started { @apply inline-flex items-center justify-center rounded-full text-sm font-medium text-primary-foreground shadow-glow px-5 py-2 transition-all mr-2; - background: linear-gradient(135deg, hsla(var(--primary) / 1) 0%, hsla(var(--primary) / 0.8) 100%); + background: linear-gradient( + 135deg, + hsla(var(--primary) / 1) 0%, + hsla(var(--primary) / 0.8) 100% + ); } .navbar__item-get_started:hover { @apply translate-y-0.5 shadow-lg; - background: linear-gradient(135deg, hsla(var(--primary) / 0.92) 0%, hsla(var(--primary) / 0.72) 100%); + background: linear-gradient( + 135deg, + hsla(var(--primary) / 0.92) 0%, + hsla(var(--primary) / 0.72) 100% + ); box-shadow: 0 18px 35px -18px hsla(var(--primary) / 0.65); color: hsl(var(--primary-foreground)); } @@ -292,12 +324,12 @@ @apply list-decimal; } -.tabs-container>ul.tabs { +.tabs-container > ul.tabs { @apply mb-0; @apply ml-0; } -.tabs-container>div { +.tabs-container > div { /* rounded bottom corners */ @apply rounded-b-lg p-4 border border-border/70 dark:border-border/60 bg-background dark:bg-card; @@ -342,12 +374,12 @@ border-bottom: 2px solid hsla(var(--primary) / 0.9); } -[data-theme='dark'] .tabs__item { +[data-theme="dark"] .tabs__item { background: hsla(var(--card) / 0.55); color: hsl(var(--muted-foreground)); } -[data-theme='dark'] .tabs__item--active { +[data-theme="dark"] .tabs__item--active { background: hsl(var(--card)); border-color: hsla(var(--border) / 0.7); color: hsl(var(--foreground)); @@ -357,11 +389,13 @@ border-bottom: 2px solid hsla(var(--primary) / 0.9); } - - footer.footer--dark { position: relative; - background-image: linear-gradient(135deg, #011d50 0%, #093e9f 100%) !important; + background-image: linear-gradient( + 135deg, + #011d50 0%, + #093e9f 100% + ) !important; background-color: #011d50 !important; color: rgba(255, 255, 255, 0.92); box-shadow: 0 -18px 48px -32px rgba(1, 29, 80, 0.6); @@ -390,15 +424,15 @@ footer.footer--dark .footer__link-item:hover { display: none !important; } -.docs-doc-id-ask main>.container { +.docs-doc-id-ask main > .container { padding: 0 !important; } -.docs-doc-id-ask article>nav { +.docs-doc-id-ask article > nav { display: none !important; } -.docs-doc-id-ask main>.container>.row>.col { +.docs-doc-id-ask main > .container > .row > .col { max-width: 100% !important; } diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 9ad0df4269..365058cebd 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,5 +1,5 @@ -import { type ClassValue, clsx } from 'clsx'; -import { twMerge } from 'tailwind-merge'; +import { type ClassValue, clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); diff --git a/src/pages/docs.tsx b/src/pages/docs.tsx index f796369e14..a33dee5841 100644 --- a/src/pages/docs.tsx +++ b/src/pages/docs.tsx @@ -1,21 +1,18 @@ -import React, { useEffect } from 'react'; +import React, { useEffect } from "react"; function DocsPage() { useEffect(() => { // Redirect to the specific documentation page - window.location.href = '/docs/intro'; + window.location.href = "/docs/intro"; }, []); // This component will only be rendered briefly before the redirect happens return (

Documentation

-

- Redirecting to the documentation... -

+

Redirecting to the documentation...

); } export default DocsPage; - diff --git a/tailwind.config.js b/tailwind.config.js index 79038694fa..ae9f5ed850 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,88 +1,89 @@ -const { fontFamily } = require('tailwindcss/defaultTheme'); +const { fontFamily } = require("tailwindcss/defaultTheme"); /** @type {import('tailwindcss').Config} */ module.exports = { - darkMode: ['class', '[data-theme="dark"]'], + darkMode: ["class", '[data-theme="dark"]'], content: [ - './src/**/*.{js,jsx,ts,tsx,mdx}', - './docs/**/*.{md,mdx}', - './blog/**/*.{md,mdx}', - './docusaurus.config.js', + "./src/**/*.{js,jsx,ts,tsx,mdx}", + "./docs/**/*.{md,mdx}", + "./blog/**/*.{md,mdx}", + "./docusaurus.config.js", ], theme: { container: { center: true, - padding: '1.5rem', + padding: "1.5rem", screens: { - '2xl': '1280px', + "2xl": "1280px", }, }, extend: { fontFamily: { - sans: ['var(--font-sans)', ...fontFamily.sans], - display: ['var(--font-display)', ...fontFamily.sans], + sans: ["var(--font-sans)", ...fontFamily.sans], + display: ["var(--font-display)", ...fontFamily.sans], }, colors: { - border: 'hsl(var(--border))', - input: 'hsl(var(--input))', - ring: 'hsl(var(--ring))', - background: 'hsl(var(--background))', - foreground: 'hsl(var(--foreground))', + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", primary: { - DEFAULT: 'hsl(var(--primary))', - foreground: 'hsl(var(--primary-foreground))', + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", }, secondary: { - DEFAULT: 'hsl(var(--secondary))', - foreground: 'hsl(var(--secondary-foreground))', + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", }, muted: { - DEFAULT: 'hsl(var(--muted))', - foreground: 'hsl(var(--muted-foreground))', + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", }, accent: { - DEFAULT: 'hsl(var(--accent))', - foreground: 'hsl(var(--accent-foreground))', + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", }, destructive: { - DEFAULT: 'hsl(var(--destructive))', - foreground: 'hsl(var(--destructive-foreground))', + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", }, popover: { - DEFAULT: 'hsl(var(--popover))', - foreground: 'hsl(var(--popover-foreground))', + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", }, card: { - DEFAULT: 'hsl(var(--card))', - foreground: 'hsl(var(--card-foreground))', + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", }, }, borderRadius: { - lg: 'var(--radius)', - md: 'calc(var(--radius) - 2px)', - sm: 'calc(var(--radius) - 4px)', + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", }, keyframes: { - 'accordion-down': { - from: { height: '0' }, - to: { height: 'var(--radix-accordion-content-height)' }, + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, }, - 'accordion-up': { - from: { height: 'var(--radix-accordion-content-height)' }, - to: { height: '0' }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, }, }, animation: { - 'accordion-down': 'accordion-down 0.2s ease-out', - 'accordion-up': 'accordion-up 0.2s ease-out', + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", }, boxShadow: { - 'glow': '0 20px 45px -20px hsla(var(--primary), 0.5)', + glow: "0 20px 45px -20px hsla(var(--primary), 0.5)", }, backgroundImage: { - 'hero-grid': 'radial-gradient(circle at 20% 20%, hsla(var(--primary),0.2) 0, transparent 35%), radial-gradient(circle at 80% 0%, hsla(var(--primary),0.18) 0, transparent 40%), radial-gradient(circle at 50% 80%, hsla(var(--secondary),0.15) 0, transparent 45%)', + "hero-grid": + "radial-gradient(circle at 20% 20%, hsla(var(--primary),0.2) 0, transparent 35%), radial-gradient(circle at 80% 0%, hsla(var(--primary),0.18) 0, transparent 40%), radial-gradient(circle at 50% 80%, hsla(var(--secondary),0.15) 0, transparent 45%)", }, }, }, - plugins: [require('tailwindcss-animate')], + plugins: [require("tailwindcss-animate")], }; diff --git a/tsconfig.json b/tsconfig.json index 4404ca59a2..0305c80b87 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,9 +4,7 @@ "compilerOptions": { "baseUrl": ".", "paths": { - "@/*": [ - "src/*" - ] + "@/*": ["src/*"] } } }