# Rafael Thayto - Full Content > Complete blog content for LLM consumption. Written in Portuguese (BR) and English. --- ## Home # Rafael Thayto > Senior Software Engineer Atualmente tenho mais de 7 anos de experiência como desenvolvedor. Desde o inicio da minha carreira lá em 2018 (quando comecei à atuar profissionalmente) sempre trabalhei com sistemas distribuídos, microsserviços, microfrontends e observabilidade. Já trabalhei em grandes empresas ([BlindPay](https://blindpay.com/), [Clerk](https://clerk.com), [Nike](https://nike.com), [Resend](https://resend.com), [Outlit (YC W25)](https://outlit.ai), [Flash](https://flashapp.com.br), [🦄 Creditas](https://creditas.com), Safra Bank ([NY](https://www.safra.com/), [BR](https://www.safra.com.br/), [Suíça](https://www.jsafrasarasin.com/)), [Avanade](https://avanade.com)) com milhões de usuarios ativos, milhões de transações diárias e que já transacionaram bilhões de dólares, empresas no Brasil, EUA e Suíça. Aqui vocês vão encontrar alguns posts sobre tecnologia e alguns pensamentos (tanto em inglês quanto em português). ## Curiosidades Fiz minha própria ferramenta de trabalho para orquestrar meus agentes, o [Thaytool](https://thaytool.com). Algumas contribuições open source que já fiz ([CLI da Clerk](https://github.com/clerk/cli), [CLI da Resend](https://github.com/resend/resend-cli), [SDK e CLI da Outlit](https://github.com/OutlitAI/outlit-sdk), [MCP Apps](https://github.com/modelcontextprotocol/ext-apps)), publiquei o [ajusta](https://www.npmjs.com/package/ajusta) e o [mdlens](https://www.npmjs.com/package/mdlens) no npm, escrevo artigos no [dev.to](https://dev.to/thayto), no [TabNews](https://www.tabnews.com.br/thayto) e no [Substack](https://thayto.substack.com/), criei o podcast [DevSenior Cast](https://creators.spotify.com/pod/profile/devseniorscast/), posto sobre tech e carreira no [X/Twitter](https://x.com/thayto_dev), no [Instagram](https://www.instagram.com/thayto_dev), no [TikTok](https://www.tiktok.com/@thayto_dev) e no [YouTube](https://www.youtube.com/@thayto_dev), e já fui tão apaixonado por perfumes que até tive um [TikTok de perfumes](https://www.tiktok.com/@thayto_perfumes) por um tempo. Se quiser mais links dá uma olhada no meu [linktree](https://thayto.com/linktree). ## Posts - [Como Deixar a Sugestão de Arquivos do Claude Code Mais Rápida](https://thayto.com/blog/claude-code-faster-file-suggestion.md) - 2026-01-08 - [Micro Frontends com Next.js Multi Zones](https://thayto.com/blog/microfrontends-next-js-multi-zones.md) - 2024-10-01 - [Deploy sem downtime com Docker Compose em VPS OCI usando GitHub Actions](https://thayto.com/blog/zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions.md) - 2024-09-13 - [Expondo seu localhost com Ngrok (muito útil para testar webhooks)](https://thayto.com/blog/expondo-seu-localhost-com-ngrok.md) - 2024-07-06 - [TypeScript - Como pegar uma string no seu Literal Types](https://thayto.com/blog/ts-como-pegar-string-no-seu-literal-types.md) - 2023-07-24 - [TypeScript - Template Literal Types](https://thayto.com/blog/typescript-template-literal-types.md) - 2023-07-07 - [Como abrir o Visual Studio Code (VSCode) dentro do GitHub](https://thayto.com/blog/como-abrir-o-vscode-direto-do-github.md) - 2022-11-05 - [Diferença entre os operadores ?, ?? e || no JavaScript / TypeScript](https://thayto.com/blog/diferenca-entre-operadores-js-ts.md) - 2022-10-31 - [Turbinando seu git bash (Windows) adicionando aliases (como se fosse o git plugin do zsh)](https://thayto.com/blog/turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh.md) - 2022-10-23 - [Como configurar o deploy do Turborepo no Netlify](https://thayto.com/blog/como-configurar-o-deploy-do-turborepo-no-netlify.md) - 2022-06-21 - [Como 'settar' a versão default do Node usando nvm](https://thayto.com/blog/como-settar-a-versao-default-do-node-usando-nvm.md) - 2022-06-19 ## Links - [Blog](https://thayto.com/blog) - [About](https://thayto.com/about) - [Books](https://thayto.com/books) - [Hobbies](https://thayto.com/hobbies) - [Linktree](https://thayto.com/linktree) --- # Rafael Thayto > Senior Software Engineer I currently have over 7 years of experience as a developer. Since the beginning of my career back in 2018 (when I started working professionally) I have always worked with distributed systems, microservices, microfrontends and observability. I have worked at large companies ([BlindPay](https://blindpay.com/), [Clerk](https://clerk.com), [Nike](https://nike.com), [Resend](https://resend.com), [Outlit (YC W25)](https://outlit.ai), [Flash](https://flashapp.com.br), [🦄 Creditas](https://creditas.com), Safra Bank ([NY](https://www.safra.com/), [BR](https://www.safra.com.br/), [Switzerland](https://www.jsafrasarasin.com/)), [Avanade](https://avanade.com)) with millions of active users, millions of daily transactions and billions of dollars transacted, companies in Brazil, USA and Switzerland. Here you will find some posts about technology and some thoughts (both in English and Portuguese). ## Fun facts I built my own work tool to orchestrate my agents, [Thaytool](https://thaytool.com). Some open source contributions I've made ([Clerk's CLI](https://github.com/clerk/cli), [Resend's CLI](https://github.com/resend/resend-cli), [Outlit's SDK and CLI](https://github.com/OutlitAI/outlit-sdk), [MCP Apps](https://github.com/modelcontextprotocol/ext-apps)), published [ajusta](https://www.npmjs.com/package/ajusta) and [mdlens](https://www.npmjs.com/package/mdlens) on npm, write articles on [dev.to](https://dev.to/thayto), [TabNews](https://www.tabnews.com.br/thayto) and [Substack](https://thayto.substack.com/), created the [DevSenior Cast](https://creators.spotify.com/pod/profile/devseniorscast/) podcast, post about tech and career on [X/Twitter](https://x.com/thayto_dev), [Instagram](https://www.instagram.com/thayto_dev), [TikTok](https://www.tiktok.com/@thayto_dev) and [YouTube](https://www.youtube.com/@thayto_dev), and I was once so passionate about perfumes that I even had a [perfume TikTok](https://www.tiktok.com/@thayto_perfumes) for a while. If you want more links, check out my [linktree](https://thayto.com/linktree). ## Posts - [Quick Guide to Make Claude Code File Suggestion Faster](https://thayto.com/en/blog/claude-code-faster-file-suggestion.md) - 2026-01-08 - [Micro Frontends with Next.js Multi Zones](https://thayto.com/en/blog/microfrontends-next-js-multi-zones.md) - 2024-10-01 - [Zero Downtime Deployment with Docker Compose in an OCI VPS using GitHub Actions](https://thayto.com/en/blog/zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions.md) - 2024-09-13 - [Exposing Your Localhost with Ngrok (Very Useful for Testing Webhooks)](https://thayto.com/en/blog/expondo-seu-localhost-com-ngrok.md) - 2024-07-06 - [TypeScript - How to Extract a String from Your Literal Types](https://thayto.com/en/blog/ts-como-pegar-string-no-seu-literal-types.md) - 2023-07-24 - [TypeScript - Template Literal Types](https://thayto.com/en/blog/typescript-template-literal-types.md) - 2023-07-07 - [How to Open Visual Studio Code (VSCode) Inside GitHub](https://thayto.com/en/blog/como-abrir-o-vscode-direto-do-github.md) - 2022-11-05 - [Difference Between the ?, ?? and || Operators in JavaScript / TypeScript](https://thayto.com/en/blog/diferenca-entre-operadores-js-ts.md) - 2022-10-31 - [Supercharging Your Git Bash (Windows) by Adding Aliases (Like the zsh git plugin)](https://thayto.com/en/blog/turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh.md) - 2022-10-23 - [How to Configure Turborepo Deployment on Netlify](https://thayto.com/en/blog/como-configurar-o-deploy-do-turborepo-no-netlify.md) - 2022-06-21 - [How to Set the Default Node Version Using nvm](https://thayto.com/en/blog/como-settar-a-versao-default-do-node-usando-nvm.md) - 2022-06-19 ## Links - [Blog](https://thayto.com/en/blog) - [About](https://thayto.com/en/about) - [Books](https://thayto.com/en/books) - [Hobbies](https://thayto.com/en/hobbies) - [Linktree](https://thayto.com/en/linktree) --- ## Blog Posts (Portuguese) ### Como Deixar a Sugestão de Arquivos do Claude Code Mais Rápida Source: https://thayto.com/blog/claude-code-faster-file-suggestion --- title: 'Como Deixar a Sugestão de Arquivos do Claude Code Mais Rápida' publishedTime: '2026-01-08T12:00:00Z' modifiedTime: '2026-01-08T12:00:00Z' description: 'Aprenda a melhorar a sugestão de arquivos do Claude Code usando fzf para fuzzy matching, suporte a symlinks e melhor performance.' image: { src: 'claude-code-faster-file-suggestion.jpg', placeholder: 'claude-code-faster-file-suggestion-small.jpg', type: 'image/jpeg', } tags: ['claude-code', 'cli', 'fzf', 'productivity', 'tutorial'] href: '/blog/claude-code-faster-file-suggestion' reactionsLength: 0 commentsLength: 0 --- A sugestão padrão de arquivos com `@` do Claude Code sempre me decepcionou, sendo acostumado com opções superiores tipo fzf. Além disso, não sugeria arquivos de pastas gitignored ou symlinks, o que era chato pra caramba. Mas recentemente a Anthropic liberou a customização via File Suggestion settings. E cara! Fuzzy matching de verdade com fzf é muito melhor do que o padrão! ## Pré-requisitos Você vai precisar instalar essas ferramentas CLI: - **rg (ripgrep)** - pra listagem rápida de arquivos com suporte a symlink - **fzf** - pro fuzzy matching - **jq** - pra parsear o JSON de entrada ## Setup ### 1. Configurar o Claude Code Adiciona isso no seu `~/.claude/settings.json`: ```json "fileSuggestion": { "type": "command", "command": "~/.claude/file-suggestion.sh" } ``` ### 2. Criar o Script de Sugestão de Arquivos Cria o arquivo `~/.claude/file-suggestion.sh` com esse conteúdo: ```bash #!/bin/bash # Custom file suggestion script for Claude Code # Uses rg + fzf for fuzzy matching and symlink support # Parse JSON input to get query QUERY=$(jq -r '.query // ""') # Use project dir from env, fallback to pwd PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}" # cd into project dir so rg outputs relative paths cd "$PROJECT_DIR" || exit 1 { # Main search - respects .gitignore, includes hidden files, follows symlinks rg --files --follow --hidden . 2>/dev/null # Additional paths - include even if gitignored (uncomment and customize) # [ -e .notes ] && rg --files --follow --hidden --no-ignore-vcs .notes 2>/dev/null } | sort -u | fzf --filter "$QUERY" | head -15 ``` ### 3. Tornar o Script Executável Não esquece de deixar o script executável: ```bash chmod +x ~/.claude/file-suggestion.sh ``` ### 4. Reiniciar o Claude Code Reinicia o Claude Code e boa! tmj :) --- ### Micro Frontends com Next.js Multi Zones Source: https://thayto.com/blog/microfrontends-next-js-multi-zones --- title: 'Micro Frontends com Next.js Multi Zones' publishedTime: '2024-10-01T16:09:33Z' modifiedTime: '2024-10-03T16:09:33Z' description: 'Quando estamos desenvolvendo uma aplicação Web e ela acaba escalando, geralmente precisamos de um time. Quando essa aplicação escala mais ainda, precisamos de mais times trabalhando na mesma aplicação.' image: { src: 'microfrontends-next-js-multi-zones.jpg', placeholder: 'microfrontends-next-js-multi-zones-small.jpg', type: 'image/jpeg', } tags: ['microfrontends', 'nextjs', 'tutorial', 'react'] href: '/blog/microfrontends-next-js-multi-zones' reactionsLength: 0 commentsLength: 0 --- ## Introdução Quando estamos desenvolvendo uma aplicação Web e ela acaba escalando, geralmente precisamos de um time. Quando essa aplicação escala mais ainda, precisamos de mais times trabalhando na mesma aplicação. Por que estou falando isso? Porque geralmente quando temos muitas pessoas alterando o mesmo projeto, podemos ter diversos problemas. Sejam eles muitos _Pull Requests_, diferenças de escopos, regras de negócio distintas, conflitos de _merge_ e até mesmo divergências de ideias. Quando isso acontece o que geralmente pensamos é em separar um pedaço dele em outro projeto e criar o famoso Micro Frontend que vai agilizar nossas entregas e garantir que o projeto evolua de maneira mais rápida e independente de outros escopos. O que facilita e muito o desenvolvimento! Com o isso o time do _Next.js_ criou uma feature avançada que chama [**Multi Zones**](https://nextjs.org/docs/advanced-features/multi-zones), com ela a gente consegue criar diversos Micro Frontends de forma muito rápida e simples utilizando _**Next.js**_ ## Pré-requisitos Antes de continuar a ler esse artigo, eu presumo que você tenha uma noção básica / familiaridade com **_Next.js_** ## O que vamos fazer Até o final desse artigo vamos fazer 2 apps com Next.js, combinar eles usando a feature de **Multi Zones**. Vou fazer também uma parte 2 mostrando como deployar as 2 aplicações na Vercel. ## Criando a pasta do projeto Abre seu terminal/cmd e manda o seguinte comando: ```bash mkdir nextjs-multi-zones-post cd nextjs-multi-zones-post ``` Logo em seguida abram essa mesma pasta no editor preferido de vocês! (nessa época era o VSCode 💩, hoje em dia é o Neovim/VIM ❤️) ## Criando os projetos Após ter entrado na pasta que vai centralizar nossos projetos, chegou a hora de criá-los! ### Primeira aplicação - Home Abre seu terminal aí e escreve o seguinte comando para criarmos o primeiro projeto que vai ser nossa **HOME**: ```bash npx create-next-app --ts home cd home ``` Depois de criar a home, vamos alterar a porta em que o projeto vai rodar pra facilitar a nossa vida e termos um padrão até o final do post Abra o arquivo `package.json` que está dentro da sua home e altere o script de dev de: ```json ... "scripts": { "dev": "next dev", ... }, ... ``` para: ```json ... "scripts": { "dev": "next dev -p 4444", ... }, ... ``` Abra seu terminal na mesma pasta e rode o seguinte comando pra executar a aplicação: ``` npm run dev ``` ### Segunda aplicação - Blog Abra outro terminal e rode o seguinte comando para criarmos o nosso segundo projeto que vai ser nosso **BLOG**: ```bash npx create-next-app --ts blog cd blog ``` Vamos seguir o mesmo procedimento que fizemos para home agora com o blog Abra o arquivo `package.json` que está dentro do seu blog e altere o script de dev de: ```json ... "scripts": { "dev": "next dev", ... }, ... ``` para: ```json ... "scripts": { "dev": "next dev -p 7777", ... }, ... ``` Abra seu terminal na mesma pasta e rode o seguinte comando pra executar a aplicação: ``` npm run dev ``` Pronto, agora já temos as 2 aplicações rodando e o resultado tem que ser algo parecido com isso: ![Resultado esperado](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iemso2388vls7qftr0b9.png) **Home (Aplicação 1)** rodando na porta **http://localhost:4444** e o **Blog (Aplicação 2)** rodando na porta **http://localhost:7777** ## Configurando a Home (Aplicação 1) O primeiro projeto que vamos desenvolver e configurar vai ser Home que além de ser o projeto base também vai ser nossa Home. É nele onde vamos configurar as URLs que vamos fazer o [rewrite](https://nextjs.org/docs/api-reference/next.config.js/rewrites) para que o Micro Frontend e as Multi Zones funcionem. Abram o arquivo `next.config.js` e adicionem as seguintes linhas de código: ```js const { BLOG_URL } = process.env const nextConfig = { async rewrites() { return [ { source: '/:path*', destination: `/:path*`, }, { source: '/blog', destination: `${BLOG_URL}/blog`, }, { source: '/blog/:path*', destination: `${BLOG_URL}/blog/:path*`, }, ] }, } ``` Na parte de `rewrites` é basicamente uma reescrita mesmo, vamos reescrever as rotas `/blog` e tudo que vier depois, como por exemplo `/blog/posts/hello-world`. Para saber mais sobre rewrites é só acessar a documentação oficial do **Next.js**. E vocês podem perceber que também adicionamos uma constante usando uma desestruturação no objeto `process.env` para podermos pegara `URL` do blog via variáveis de ambiente. Agora bora criar o arquivo `.env` na raiz do nosso projeto da `home` como mostra no exemplo abaixo. ![Onde vai ficar localizado o arquivo .env](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4ky9svj4jt4txy51zh8.png) É nele onde vamos colocar o `BLOG_URL` apontando para o `http://localhost:7777` que é onde está rodando nosso projeto do blog. `.env` ```txt BLOG_URL=http://localhost:7777 ``` Agora dentro do nosso projeto da home vamos transformar o `index.tsx` nisso daqui: ```tsx import type { NextPage } from 'next' import Head from 'next/head' import Image from 'next/image' import Link from 'next/link' import styles from '../styles/Home.module.css' const Home: NextPage = () => { return (
Next.js Blog With Multi Zones

Welcome to Home! :D

Go Blog →

Click here and go home on our other Next.js app :D

About us →

Click here and go to About Us page!

Go to Hello post →

Click here and go to the Hello Hello post

) } export default Home ``` Basicamente adicionamos 3 links, sendo 2 deles para páginas do nosso blog (ainda não construído) e 1 link para o redirecionamento para uma página interna nossa que é a _About Us_. ![Home page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5w3xa558sm1z0c5qmdf5.png) Agora vamos criar o arquivo `about.tsx` dentro de `home/pages/about.tsx`: ![Local onde vai ficar o nosso arquivo about.tsx](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7701xk00103lwhr83k8t.png) `about.tsx` ```tsx import type { NextPage } from 'next' import Link from 'next/link' const About: NextPage = () => { return (

About us :O


Go home
) } export default About ``` Aqui criamos uma página sobre nós bem simples que só tem um texto e um link que nos faz voltar para a home. E assim finalizamos a parte da **Home**. Agora bora pro Blog! ## Configurando o Blog (Aplicação 2) Agora vamos configurar o blog. No blog vai ser um pouco mais simples, basicamente vamos abrir o arquivo `next.config.js` e adicionar a propriedade `basePath` dentro do `nextConfig`, ele vai dizer para o next que a _URL_ base dele vai começar no endereço que escolhermos, que nesse caso vai ser `/blog` `next.config.js` ```js const nextConfig = { basePath: '/blog', } module.exports = nextConfig ``` > Lembrando que toda vez que alteramos o arquivo **next.config.js** ou adicionamos algo no **.env** precisamos parar o servidor e inicia-lo novamente 😁 Agora dentro do nosso projeto da blog vamos transformar o `index.tsx` nisso daqui: ```tsx import type { NextPage } from 'next' import Head from 'next/head' import Image from 'next/image' import Link from 'next/link' import styles from '../styles/Home.module.css' const Blog: NextPage = () => { return (
Next.js Blog With Multi Zones

Welcome to Blog!

Go Home →

Click here and go home on our other Next.js app :D

Go to It Works post →

Click here and go to the It Works post

Go to Hello post →

Click here and go to the Hello Hello post

) } export default Blog ``` Na raiz do nosso blog temos 1 link que leva para nossa **Home** e 2 links que nos levam a posts no nosso blog ![Blog page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/buxvf0bmqx9e3mnj4erj.png) Na imagem acima vocês já conseguem ver a mágica acontecendo, se vocês olharem para o URL vão perceber que estamos rodando no **http://localhost:4444/blog**, mas por quê isso acontece? Quando configuramos o `basePath` dentro do `next.config.js` do projeto **Blog**, dizemos para o next que o `index.tsx` e todos os outros arquivos vão ser acessados sempre a partir do nosso `basePath` que nesse caso é `/blog`. Também devemos nos lembrar que no `next.config.js` do nosso projeto **Home** configuramos que sempre que estivermos em **http://localhost:4444** e acessarmos o `/blog` ou qualquer coisa depois de blog, estaremos fazendo um rewrite da rota e estaremos apontando para o nosso **Blog** via **Home**. > Ou seja: http://localhost:4444/blog vai apontar para http://localhost:7777/blog O mais incrível de tudo isso é que conseguimos acessar a nossa outra aplicação (blog) dentro da nossa aplicação (home) **sem alterar a URL** 🤯, não é impressionante? _Cover Image: Photo by Andrey Tikhonovskiy on Unsplash_ --- ### Deploy sem downtime com Docker Compose em VPS OCI usando GitHub Actions Source: https://thayto.com/blog/zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions --- title: 'Deploy sem downtime com Docker Compose em VPS OCI usando GitHub Actions' publishedTime: '2024-09-13T21:16:36Z' modifiedTime: '2024-09-13T21:16:36Z' description: 'Primeiro, você precisa ter um VPS Linux (vou usar Ubuntu 22 nesse tutorial). Tags: docker, devops, github actions, linux, tutorial.' image: { src: 'zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions.jpg', placeholder: 'zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions-small.jpg', type: 'image/jpeg', } tags: ['docker', 'devops', 'github actions', 'linux', 'tutorial'] href: '/blog/zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions' reactionsLength: 0 commentsLength: 0 --- Primeiro, você precisa ter um VPS Linux (vou usar Ubuntu 22 nesse tutorial). ## Requisitos - Docker - Docker Compose - [Docker Rollout Plugin](https://github.com/Wowu/docker-rollout?tab=readme-ov-file#installation) ### Instalando o Docker Rollout Plugin É bem simples: ```bash # Create directory for Docker cli plugins mkdir -p ~/.docker/cli-plugins # Download docker-rollout script to Docker cli plugins directory curl https://raw.githubusercontent.com/wowu/docker-rollout/master/docker-rollout -o ~/.docker/cli-plugins/docker-rollout # Make the script executable chmod +x ~/.docker/cli-plugins/docker-rollout ``` ### Como usar o Docker Rollout Rode `docker rollout ` ao invés de `docker compose up -d ` para atualizar um serviço sem downtime. ```bash $ docker rollout -f docker-compose.yml ``` ## Workflow GitHub Actions build_and_deploy.yml > Nesse caso estou usando um registry privado no Digital Ocean. ```yaml name: CI # 1 # Controls when the workflow will run on: # Triggers the workflow on push events but only for the master branch push: branches: [main] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: inputs: version: description: 'Image version' required: true #2 env: REGISTRY: 'registry.digitalocean.com/YOUR_REGISTRY_NAME' IMAGE_NAME: 'YOUR_IMAGE_NAME' #3 jobs: build_and_push: runs-on: ubuntu-latest steps: - name: Checkout the repo uses: actions/checkout@v4 - name: Build container image run: docker build -t $(echo $REGISTRY)/$(echo $IMAGE_NAME):$(echo $GITHUB_SHA | head -c7) . - name: Install doctl uses: digitalocean/action-doctl@v2 with: token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} - name: Log in to DigitalOcean Container Registry with short-lived credentials run: doctl registry login --expiry-seconds 600 - name: Remove all old images run: if [ ! -z "$(doctl registry repository list | grep "$(echo $IMAGE_NAME)")" ]; then doctl registry repository delete-manifest $(echo $IMAGE_NAME) $(doctl registry repository list-tags $(echo $IMAGE_NAME) | grep -o "sha.*") --force; else echo "No repository"; fi - name: Push image to DigitalOcean Container Registry run: docker push $(echo $REGISTRY)/$(echo $IMAGE_NAME):$(echo $GITHUB_SHA | head -c7) deploy: runs-on: ubuntu-latest needs: build_and_push steps: - name: Deploy to Digital Ocean droplet via SSH action uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.OCI_HOST }} username: ${{ secrets.OCI_USERNAME }} key: ${{ secrets.OCI_SSH_KEY }} envs: IMAGE_NAME,REGISTRY,{{ secrets.DIGITALOCEAN_ACCESS_TOKEN }},GITHUB_SHA script: | whoami cd ~/app pwd ls -la # Login to registry docker login -u ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} -p ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} registry.digitalocean.com # Update the APP image in docker-compose.yml sed -i '0,/image:/s|image: .*|image: '$(echo $REGISTRY)'/'$(echo $IMAGE_NAME)':'$(echo $GITHUB_SHA | head -c7)'|' docker-compose.yml # Pull the latest image docker compose pull # Here is the magic docker plugin docker rollout -f docker-compose.yml app echo "Deployed with success to production" ``` ## Conclusão Espero que esse tutorial te ajude de alguma forma, tmj! Rafael Thayto. _Photo by Bernd 📷 Dittrich on Unsplash_ --- ### Expondo seu localhost com Ngrok (muito útil para testar webhooks) Source: https://thayto.com/blog/expondo-seu-localhost-com-ngrok --- title: 'Expondo seu localhost com Ngrok (muito útil para testar webhooks)' publishedTime: '2024-07-06T15:58:51Z' modifiedTime: '2024-07-06T15:58:51Z' description: 'Estava precisando fazer alguns testes de webhooks, porém só conhecia uma maneira que seria deployar a app e só assim conseguir testar os webhooks. Esse método funciona e é muito utilizado por devs. Mas ele tem um problema... todo teste você vai precisar deployar novamente sua aplicação e isso leva bastante tempo.' image: { src: 'expondo-seu-localhost-com-ngrok.jpg', placeholder: 'expondo-seu-localhost-com-ngrok-small.jpg', type: 'image/jpeg', } tags: ['ngrok', 'webhooks', 'tutorial'] href: '/blog/expondo-seu-localhost-com-ngrok' reactionsLength: 0 commentsLength: 0 --- > Os problemas de "na minha máquina funciona" acabaram! ## Problema Estava precisando fazer alguns testes de webhooks, porém só conhecia uma maneira que seria deployar a app e só assim conseguir testar os webhooks. Esse método funciona e é muito utilizado por devs. Mas ele tem um problema... todo teste você vai precisar deployar novamente sua aplicação e isso leva bastante tempo. Pesquisando por aí encontrei o [Ngrok](https://ngrok.com/) e com ele conseguimos expor nosso localhost de forma bem simples. ## Instalação > você pode seguir a [Documentação Oficial](https://ngrok.com/docs/guides/getting-started/#step-2-install-the-ngrok-agent) também. ### Mac Use o [Homebrew](https://brew.sh/): ```bash brew install ngrok/ngrok/ngrok ``` ### Linux Use o apt ```bash curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | \ sudo gpg --dearmor -o /etc/apt/keyrings/ngrok.gpg && \ echo "deb [signed-by=/etc/apt/keyrings/ngrok.gpg] https://ngrok-agent.s3.amazonaws.com buster main" | \ sudo tee /etc/apt/sources.list.d/ngrok.list && \ sudo apt update && sudo apt install ngrok ``` ### Windows Use o [Chocolatey](https://chocolatey.org/install) ```bash choco install ngrok ``` ## Conecte o seu agent à sua conta do ngrok Pra isso é necessário que você faça o [log in ou sign up](https://dashboard.ngrok.com/) no Ngrok e pegue seu Authtoken. Copie o token e adicione no seu terminal ```bash ngrok config add-authtoken ``` ## Rode o Ngrok Starte o Ngrok rodando o comando abaixo (você pode escolher a porta que preferir, mas no exemplo utilizei a porta `8000`): ```bash ngrok http 8000 ``` Obrigado por ler até aqui! Espero que esse post te ajude :) Confira mais em https://thayto.com/links _Cover Image: Photo by Stepan Kalinin on Unsplash_ --- ### TypeScript - Como pegar uma string no seu Literal Types Source: https://thayto.com/blog/ts-como-pegar-string-no-seu-literal-types --- title: 'TypeScript - Como pegar uma string no seu Literal Types' publishedTime: '2023-07-24T15:58:51Z' modifiedTime: '2024-07-07T21:33:51Z' description: 'Não vou enrolar nada. Basicamente é só você utilizar o Utility Type Extract do próprio TypeScript e boa!' image: { src: 'ts-como-pegar-string-no-seu-literal-types.jpg', placeholder: 'ts-como-pegar-string-no-seu-literal-types-small.jpg', type: 'image/jpeg', } tags: ['typescript', 'utilities', 'types', 'tutorial'] href: '/blog/ts-como-pegar-string-no-seu-literal-types' reactionsLength: 0 commentsLength: 0 --- Salve, salve devs seniors. Não vou enrolar nada. Basicamente é só você utilizar o _Utility Type_ `Extract` do próprio **TypeScript** e boa! _**Por exemplo:**_ ```ts type CatNames = 'Garfield' | 'Yoru' | 'Yuki' | 'Fluffy' | 'Lady' | 'Lucky' type MyCatNames = Extract // |-> Extract these two `literal types` from CatNames `literal type` type Cat = { age: number name: CatNames } function getMyCat(age: number, name: MyCatNames): Cat { // |--> only show 'Yoru' and // 'Yuki' return { age, name, } as Cat } const myCat1 = getMyCat(1, 'Yoru') const myCat2 = getMyCat(1, 'Yuki') const otherCat: Cat = { age: 3, name: 'Garfield', } const otherCatWithMyCatName: Cat = { age: 5, name: 'Yoru', } // tmj! ``` > link do [TS Playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBAwghsAcnAthAzlAvFA5AcTgCcAzASwgBsATXKAHzwE0B7IgVzsdyfYGsyXPADFK7EiRBDcAGTjUpDPDPYBjPlIBQm0JCgBZEPCSoM2KAFEAHsCJxVwADzHkadABpmbTkp79BAHyaUCGhYeERkVEA9NEMALQBljZ2DlDAABYY0MAA7ixQAAaUZMAQdpTp4BiFUCRELCiwCK5mxaXlcJW6EIXaPc3A5gDewVBwAOYQAFxQAHbsKABG5e5jc6azLqbomgC+2iTscw5kLHNQU8CGxgAUkzPziytEnhtoszctOwCUWwhQUZRYEgsKxBKJKDnSggKDoDIsXJeDh0OBzahQMag7ERcE4kJ+AS4MZECDAdhEC5A0IPNZhd4QOlQPbjTDGfbaVTndBDFBGBAARnMVy+wFuAs8PG8uB+mi5cx5UD5xgATMKyaLxZLeETZXLuUMWJlysZ-kMcNSQg9ZgBmJkhBmzAjEchUWgc+WKo1ZIjGADqpQyotaZpGWPGU1mAFZ7fNNsjOPsgA) espero que ajude alguém! (OBS: esse post foi escrito em no máximo 10 minutos) meus links: https://thayto.com/linktree _Photo by Karina Vorozheeva on Unsplash_ --- ### TypeScript - Template Literal Types Source: https://thayto.com/blog/typescript-template-literal-types --- title: 'TypeScript - Template Literal Types' publishedTime: '2023-07-07T07:30:51Z' modifiedTime: '2024-07-07T21:08:51Z' description: 'Template Literal Types é um bglh insano que expande as possibilidades do que já era possível com o string literals + unions do TS, dá uma olhada! Ele tem a mesma sintaxe do template literal string do JavaScript.' image: { src: 'typescript-template-literal-types.png', placeholder: 'typescript-template-literal-types-small.png', type: 'image/png', } tags: ['typescript', 'utilities', 'types', 'learning'] href: '/blog/typescript-template-literal-types' reactionsLength: 0 commentsLength: 0 --- ## O que é Template Literal Types? **Template Literal Types** é um bglh insano que expande as possibilidades do que já era possível com o `string literals + unions` do TS, dá uma olhada! Ele tem a mesma sintaxe do template literal string do JavaScript. ```ts type Tchau = 'tchau!' type Salve = `Oi ${Tchau}` ``` Com ele é possivel usar sua necessidade + imaginação pra fazer diversas coisas bacanas que vão agregar muito no seu projeto/type. Um exemplo bacana seria padronizar que todas as propriedades extras que adicionarmos à um type, necessitem de ter o prefixo "data\_" e o suffixo "\_prop", assim conseguimos criar um objeto com um padrão que vai facilitar o consumo de leitura para sabermos quais as propriedades extras que foram enviadas. Falar é fácil, mas cadê o exemplo? vamos começar criando um type EventData, ele é a nossa base para nosso objeto evento de evento. ```ts type EventData = { id: string name: string step: 'first_step' | 'second_step' } ``` Ele já possui propriedades definidas, que não podem ser alteradas, mas agora precisamos enviar mais alguns campos relacionados aos fields que ele tem em cada step, poderiamos extender o type EventData e adicionar os campos, mas ao invés disso, bora usar o Template Literal Types! ```ts type FormFields = { name: string age: number } type EventDataExtraProps = `field_${string & keyof T}` // aqui vamos modificar um pouco o EventData type EventData = { id: string name: string step: 'first_step' | 'second_step' } & Record, any> const event: EventData = { id: 'sextou?', name: 'thayto', step: 'first_step', field_name: 'full_name', field_age: 22, } ``` Parece complexo, mas é bem simples, em projetos grandes / libs, com um único utilitário desses, conseguimos fazer mágica nos auto_completes e na padronização do envio de determinadas propriedades! Espero que tenham aprendido algo, se não aprendeu, pelo menos leu até o final, tmj! https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html ![imagem te chamando de burro se você ainda não conhece o template literal types](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12fb3fct25e3hcwbhr9d.png) --- ### Como abrir o Visual Studio Code (VSCode) dentro do GitHub Source: https://thayto.com/blog/como-abrir-o-vscode-direto-do-github --- title: 'Como abrir o Visual Studio Code (VSCode) dentro do GitHub' publishedTime: '2022-11-05T15:16:36Z' modifiedTime: '2024-07-07T20:43:36Z' description: 'Todo dev já precisou em algum momento editar um arquivo dentro do GitHub e se deparou com essa tela de edição padrão do próprio GitHub' image: { src: 'como-abrir-o-vscode-direto-do-github.png', placeholder: 'como-abrir-o-vscode-direto-do-github-small.png', type: 'image/png', } tags: ['github', 'tutorial', 'vscode'] href: '/blog/como-abrir-o-vscode-direto-do-github' reactionsLength: 12 commentsLength: 0 --- ## De forma rápida: Basta apertar a tecla `.` dentro do seu repositório de preferência > no final do post tem algumas outras maneiras de abrir o github.dev também! ## TL;DR Todo dev já precisou em algum momento editar um arquivo dentro do GitHub e se deparou com essa tela de edição padrão do próprio GitHub: ![Tela de edição padrão do GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i3kximxr5ck48anftm7b.png) No primeiro momento ela parece uma tela "OK" para fazermos uma edição rápida, mas quando precisamos pesquisar mais coisas dentro do repositório, essa tela não nos ajuda muito e é por isso que vou mostrar como abrir o Visual Studio Code(VSCode) direto do GitHub. É bem simples, basta apertar a tecla `.` **(ponto)** do seu teclado e voilá, vai abrir magicamente um VSCode direto do seu navegador e dentro do seu repositório com quase todas as funcionalidades que o VSCode tem. ![Tela de carregamento do VSCode dentro do navegador](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p3fkli1ddemzshin31m6.png) ![VSCode aberto dentro do navegador mostrando o repositório](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oaiou6k7vvaufog1rsgh.png) Agora você consegue fazer quantas pesquisas quiser, mexer em quantos arquivos forem necessários e também vai poder fazer commits, instalar extensões, mudar o tema, entre outras várias funcionalidades. ## Outras maneiras de abrir o VSCode direto do GitHub Outras maneiras de abrir qualquer repo do GitHub no `github.dev` (VSCode do GitHub) são: - Alterando a URL de `github.com` para `github.dev` - Apertando a tecla `.` dentro de algum repositório - Ao abrir um arquivo dentro de algum repositório, selecione o menu suspenso ▼ e clique em abrir com (open with) `github.dev`. ![Print da tela do github que mostra o menu suspenso e o botão de abrir com](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jinx6wgkatgmocwaubzb.png) Essa foi a dica de hoje, espero ter ajudado, tmj! 😎 --- ### Diferença entre os operadores ?, ?? e || no JavaScript / TypeScript Source: https://thayto.com/blog/diferenca-entre-operadores-js-ts --- title: 'Diferença entre os operadores ?, ?? e || no JavaScript / TypeScript' publishedTime: '2022-10-31T19:34:48Z' modifiedTime: '2024-07-11T19:34:53Z' description: '? -> Optional Chaining Operator. Permite a leitura do valor de uma propriedade localizada internamente em uma cadeia de objetos conectados, sem que a validação de cada referência da cadeia seja expressivamente realizada.' image: { src: 'diferenca-entre-operadores-js-ts.png', placeholder: 'diferenca-entre-operadores-js-ts-small.png', type: 'image/png', } tags: ['javascript', 'typescript', 'learning'] href: '/blog/diferenca-entre-operadores-js-ts' reactionsLength: 0 commentsLength: 0 --- # Contexto Acredito que todo dev já ficou em dúvida sobre as diferenças entre alguns operadores do JavaScript/TypeScript. Por isso fiz esse post simples e rápido sobre as diferenças entre os operadores ?, ?? e ||. Confira logo abaixo ## ? - Optional Chaining `?` -> **Optional Chaining Operator**. Permite a leitura do valor de uma propriedade localizada internamente em uma cadeia de objetos conectados, sem que a validação de cada referência da cadeia seja expressivamente realizada. O operador `?.` funciona de maneira similar ao operador `.` de encadeamento, exceto que, ao invés de causar um erro se a referência é [nullish](https://developer.mozilla.org/en-US/docs/Glossary/Nullish) (`null` ou `undefined`), a expressão sofre um "curto-circuito" e retorna com um valor de `undefined`. Quando utilizado com uma chamada de função, retorna `undefined` se a função executada não existir. Isso resulta em expressões mais curtas e simples ao acessar propriedades encadeadas quando a possibilidade de uma referência ser inexistente. Isso também pode auxiliar ao explorar o conteúdo de um objeto quando não existe garantia da existência de determinadas propriedades obrigatórias. **Exemplo** ```js const user = { id: 13, name: 'Thayto', } console.log(user?.name) // John console.log(user?.fullName) // undefined, aplicação não vai quebrar. console.log(user.fullName) // TypeError: Cannot read property ‘fullName’ of undefined, aplicação quebra. ``` ## ?? - Nullish Coalesing `??` -> **Nullish Coalescing Operator**. É um operador lógico que retorna o seu operando do lado direito quando o seu operador do lado esquerdo é `null` ou `undefined`. Caso contrário, ele retorna o seu operando do lado esquerdo. **Exemplo** ```js console.log(13 ?? 'não encontrado') // 13 console.log(0 ?? 'não encontrado') // 0 console.log('Dri' ?? 'não encontrado') // "Dri" console.log('' ?? 'não encontrado') // "" console.log(true ?? 'não encontrado') // true console.log(false ?? 'não encontrado') // false console.log(undefined ?? 'não encontrado') // "não encontrado" console.log(null ?? 'não encontrado') // "não encontrado" ``` ## || - Logical OR `||` é o **Logical OR Operator** A expressão Logical OR é avaliada da esquerda para a direita, é testada para possível avaliação de "curto-circuito" usando a seguinte regra: `(alguma truthy expression) || expr` é avaliado em curto-circuito para a expressão verdadeira. Curto-circuito significa que a parte `expr` acima não é avaliada, portanto, quaisquer efeitos colaterais de fazê-lo não têm efeito (por exemplo, se `expr` é uma chamada de função, a chamada nunca ocorre). Isso acontece porque o valor do operador já é determinado após a avaliação do primeiro operando. **Exemplo** ```js console.log(13 || 'não encontrado') // 13 console.log(0 || 'não encontrado') // "não encontrado" console.log('Dri' || 'não encontrado') // "Dri" console.log('' || 'não encontrado') // "não encontrado" console.log(true || 'não encontrado') // true console.log(false || 'não encontrado') // "não encontrado" console.log(undefined || 'não encontrado') // "não encontrado" console.log(null || 'não encontrado') // "não encontrado" ``` --- ### Turbinando seu git bash (Windows) adicionando aliases (como se fosse o git plugin do zsh) Source: https://thayto.com/blog/turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh --- title: 'Turbinando seu git bash (Windows) adicionando aliases (como se fosse o git plugin do zsh)' publishedTime: '2022-10-23T18:55:46Z' modifiedTime: '2022-10-23T18:55:46Z' description: 'Nesse guia simples vou mostrar como turbinar o seu git bash com aliases que vão facilitar e agilizar muito na hora de fazer seus commits, pulls, pegarem logs, trocarem de branch, rebases, merges, etc..' image: { src: 'turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh.jpg', placeholder: 'turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh-small.jpg', type: 'image/jpeg', } tags: ['git', 'bash', 'tutorial', 'windows'] href: '/blog/turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh' reactionsLength: 0 commentsLength: 0 --- Nesse guia simples vou mostrar como turbinar o seu git bash com aliases que vão facilitar e agilizar muito na hora de fazer seus commits, pulls, pegarem logs, trocarem de branch, rebases, merges, etc.. > Primeiro pra quem não conhece, o famoso plugin de git do zsh é esse: [zsh git plugin](https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/git/git.plugin.zsh) Bom o primeiro passo para adicionar esses aliases é você ter o [git bash](https://git-scm.com/download/win) instalado. E para adicionar os aliases é bem simples basta acessar a pasta onde estão os profiles do git bash (lembrando que esse guia é somente para **Windows**), que vai estar no seguinte caminho: `C:\Program Files\Git\etc\profile.d` ![Selecionando o arquivo aliases](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u76fyz7yqeztqzk54erg.png) Depois você vai procurar o arquivo `aliases.sh` e abrir no seu editor de texto preferido (pode até ser o notepad) e adicionar as seguintes linhas: ```bash alias g=git alias ga='git add' alias gaa='git add --all' alias gam='git am' alias gama='git am --abort' alias gamc='git am --continue' alias gams='git am --skip' alias gamscp='git am --show-current-patch' alias gap='git apply' alias gapa='git add --patch' alias gapt='git apply --3way' alias gau='git add --update' alias gav='git add --verbose' alias gb='git branch' alias gbD='git branch -D' alias gba='git branch -a' alias gbd='git branch -d' alias gbda='git branch --no-color --merged | command grep -vE "^([+*]|\s*($(git_main_branch)|$(git_develop_branch))\s*$)" | command xargs git branch -d 2>/dev/null' alias gbl='git blame -b -w' alias gbnm='git branch --no-merged' alias gbr='git branch --remote' alias gbs='git bisect' alias gbsb='git bisect bad' alias gbsg='git bisect good' alias gbsr='git bisect reset' alias gbss='git bisect start' alias gc='git commit -v' alias 'gc!'='git commit -v --amend' alias gca='git commit -v -a' alias 'gca!'='git commit -v -a --amend' alias gcam='git commit -a -m' alias 'gcan!'='git commit -v -a --no-edit --amend' alias 'gcans!'='git commit -v -a -s --no-edit --amend' alias gcas='git commit -a -s' alias gcasm='git commit -a -s -m' alias gcb='git checkout -b' alias gcd='git checkout $(git_develop_branch)' alias gcf='git config --list' alias gcl='git clone --recurse-submodules' alias gclean='git clean -id' alias gcm='git checkout $(git_main_branch)' alias gcmsg='git commit -m' alias 'gcn!'='git commit -v --no-edit --amend' alias gco='git checkout' alias gcor='git checkout --recurse-submodules' alias gcount='git shortlog -sn' alias gcp='git cherry-pick' alias gcpa='git cherry-pick --abort' alias gcpc='git cherry-pick --continue' alias gcs='git commit -S' alias gcsm='git commit -s -m' alias gcss='git commit -S -s' alias gcssm='git commit -S -s -m' alias gd='git diff' alias gdca='git diff --cached' alias gdct='git describe --tags $(git rev-list --tags --max-count=1)' alias gdcw='git diff --cached --word-diff' alias gds='git diff --staged' alias gdt='git diff-tree --no-commit-id --name-only -r' alias gdup='git diff @{upstream}' alias gdw='git diff --word-diff' alias gf='git fetch' alias gfa='git fetch --all --prune --jobs=10' alias gfg='git ls-files | grep' alias gfo='git fetch origin' alias gg='git gui citool' alias gga='git gui citool --amend' alias ggpull='git pull origin "$(git_current_branch)"' alias ggpur=ggu alias ggpush='git push origin "$(git_current_branch)"' alias ggsup='git branch --set-upstream-to=origin/$(git_current_branch)' alias ghh='git help' alias gignore='git update-index --assume-unchanged' alias gignored='git ls-files -v | grep "^[[:lower:]]"' alias git-svn-dcommit-push='git svn dcommit && git push github $(git_main_branch):svntrunk' alias gk='\gitk --all --branches &!' alias gke='\gitk --all $(git log -g --pretty=%h) &!' alias gl='git pull' alias glg='git log --stat' alias glgg='git log --graph' alias glgga='git log --graph --decorate --all' alias glgm='git log --graph --max-count=10' alias glgp='git log --stat -p' alias glo='git log --oneline --decorate' alias globurl='noglob urlglobber ' alias glod='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ad) %C(bold blue)<%an>%Creset'\' alias glods='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ad) %C(bold blue)<%an>%Creset'\'' --date=short' alias glog='git log --oneline --decorate --graph' alias gloga='git log --oneline --decorate --graph --all' alias glol='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ar) %C(bold blue)<%an>%Creset'\' alias glola='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ar) %C(bold blue)<%an>%Creset'\'' --all' alias glols='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ar) %C(bold blue)<%an>%Creset'\'' --stat' alias glp=_git_log_prettily alias gluc='git pull upstream $(git_current_branch)' alias glum='git pull upstream $(git_main_branch)' alias gm='git merge' alias gma='git merge --abort' alias gmom='git merge origin/$(git_main_branch)' alias gmtl='git mergetool --no-prompt' alias gmtlvim='git mergetool --no-prompt --tool=vimdiff' alias gmum='git merge upstream/$(git_main_branch)' alias gp='git push' alias gpd='git push --dry-run' alias gpf='git push --force-with-lease' alias 'gpf!'='git push --force' alias gpoat='git push origin --all && git push origin --tags' alias gpr='git pull --rebase' alias gpristine='git reset --hard && git clean -dffx' alias gpsup='git push --set-upstream origin $(git_current_branch)' alias gpu='git push upstream' alias gpv='git push -v' alias gr='git remote' alias gra='git remote add' alias grb='git rebase' alias grba='git rebase --abort' alias grbc='git rebase --continue' alias grbd='git rebase $(git_develop_branch)' alias grbi='git rebase -i' alias grbm='git rebase $(git_main_branch)' alias grbo='git rebase --onto' alias grbom='git rebase origin/$(git_main_branch)' alias grbs='git rebase --skip' alias grep='grep --color=auto --exclude-dir={.bzr,CVS,.git,.hg,.svn,.idea,.tox}' alias grev='git revert' alias grh='git reset' alias grhh='git reset --hard' alias grm='git rm' alias grmc='git rm --cached' alias grmv='git remote rename' alias groh='git reset origin/$(git_current_branch) --hard' alias grrm='git remote remove' alias grs='git restore' alias grset='git remote set-url' alias grss='git restore --source' alias grst='git restore --staged' alias grt='cd "$(git rev-parse --show-toplevel || echo .)"' alias gru='git reset --' alias grup='git remote update' alias grv='git remote -v' alias gsb='git status -sb' alias gsd='git svn dcommit' alias gsh='git show' alias gsi='git submodule init' alias gsps='git show --pretty=short --show-signature' alias gsr='git svn rebase' alias gss='git status -s' alias gst='git status' alias gsta='git stash push' alias gstaa='git stash apply' alias gstall='git stash --all' alias gstc='git stash clear' alias gstd='git stash drop' alias gstl='git stash list' alias gstp='git stash pop' alias gsts='git stash show --text' alias gstu='gsta --include-untracked' alias gsu='git submodule update' alias gsw='git switch' alias gswc='git switch -c' alias gswd='git switch $(git_develop_branch)' alias gswm='git switch $(git_main_branch)' alias gtl='gtl(){ git tag --sort=-v:refname -n -l "${1}*" }; noglob gtl' alias gts='git tag -s' alias gtv='git tag | sort -V' alias gunignore='git update-index --no-assume-unchanged' alias gunwip='git log -n 1 | grep -q -c "\-\-wip\-\-" && git reset HEAD~1' alias gup='git pull --rebase' alias gupa='git pull --rebase --autostash' alias gupav='git pull --rebase --autostash -v' alias gupom='git pull --rebase origin $(git_main_branch)' alias gupomi='git pull --rebase=interactive origin $(git_main_branch)' alias gupv='git pull --rebase -v' alias gwch='git whatchanged -p --abbrev-commit --pretty=medium' alias gwip='git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify --no-gpg-sign -m "--wip-- [skip ci]"' ``` Pronto! Agora você pode ser mais feliz utilizando o git de forma mais ágil e escrevendo bem menos com esses aliases! :D --- ### Como configurar o deploy do Turborepo no Netlify Source: https://thayto.com/blog/como-configurar-o-deploy-do-turborepo-no-netlify --- title: 'Como configurar o deploy do Turborepo no Netlify' publishedTime: '2022-06-21T15:14:48Z' modifiedTime: '2022-10-25T23:50:06Z' description: 'Um dia desses passei por uma dificuldade que foi fazer o deploy de uma aplicação minha utilizando Turborepo com Next.js e como acredito que mais pessoas possam ter esse problema, resolvi fazer um guia de como fazer as configurações iniciais, bora lá?' image: { src: 'como-configurar-o-deploy-do-turborepo-no-netlify.png', placeholder: 'como-configurar-o-deploy-do-turborepo-no-netlify-small.png', type: 'image/png', } tags: ['turborepo', 'netlify', 'tutorial', 'react', 'next.js'] href: '/blog/como-configurar-o-deploy-do-turborepo-no-netlify' reactionsLength: 0 commentsLength: 0 --- Um dia desses passei por uma dificuldade que foi fazer o deploy de uma aplicação minha utilizando Turborepo com Next.js e como acredito que mais pessoas possam ter esse problema, resolvi fazer um guia de como fazer as configurações iniciais, bora lá? ## Setup / Configurações iniciais Bom primeiro vamos começar com o básico mesmo, criar uma aplicação utilizando o Turborepo, que é bem simples, basta você executar esse comando abaixo no terminal :) ```bash npx create-turbo@latest ``` E aí provavelmente vai aparecer algo do tipo: ```bash Need to install the following packages: create-turbo@1.2.16 Ok to proceed? (y) ``` Basta você apertar `enter ⏎` ou `y` que ele vai baixar essa dependência pra você poder criar o projeto de uma forma bem mais simples. Logo após vai algo parecido com isso: ```bash >>> TURBOREPO >>> Welcome to Turborepo! Let's get you set up with a new codebase. ? Where would you like to create your turborepo? (./my-turborepo) ``` E nesse caso eu apertei `enter ⏎` pra ele usar esse nome padrão mesmo. Depois ele vai pedir pra você utilizar o package manager de sua preferência: ```bash ? Which package manager do you want to use? (Use arrow keys) ❯ npm pnpm yarn ``` Eu escolhi o `npm` mesmo pra facilitar a vida de vocês :D Após tudo isso provavelmente na tela de vocês vai ter algo assim: ```bash >>> TURBOREPO >>> Welcome to Turborepo! Let's get you set up with a new codebase. ? Where would you like to create your turborepo? ./my-turborepo ? Which package manager do you want to use? npm >>> Creating a new turborepo with the following: - apps/web: Next.js with TypeScript - apps/docs: Next.js with TypeScript - packages/ui: Shared React component library - packages/eslint-config-custom: Shared configuration (ESLint) - packages/tsconfig: Shared TypeScript `tsconfig.json` > Installing dependencies... ``` E por fim vai estar tudo instalado lindamente e a gente vai poder ir pra parte que interessa. 😎. Vamos abrir a pasta com nosso editor de código preferido (no meu caso é o VSCode) e quando a gente abrir a gente vai ter algo parecido com isso na estrutura de pastas: ![Imagem mostrando como deveria ficar a estrutura do projeto](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fizlxcl9y35xg8ixxxld.png) ## Criando arquivo de configuração do Netlify Após verificar que está tudo certo, vamos criar um arquivo chamado `netlify.toml` dentro do `./apps/web` ![Imagem mostrando onde deve ficar o arquivo que criamos](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwu7606izt80mx6u6bwx.png) E dentro do `netlify.toml` vamos colocar o seguinte código: ```toml [build] command = "cd ../.. && npm install && npm run build" publish = ".next" [[plugins]] package = "@netlify/plugin-nextjs" ``` Tá, mas pra que isso? Bom, isso é basicamente o arquivo de configuração que o Netlify olha antes de fazer o deployment. O `[build]` é a etapa que o Netlify olha para saber como vai fazer o build O `[[plugins]]` é a etapa que antes de fazer o build ele instala alguns plugins que vão facilitar o deployment internamente. No nosso caso é de extrema importância adicionar o plugin do `Next.js` para não termos nenhuma complicação na hora dele criar o cache interno e utilizar o `SSR` e `Edge Functions` do Next. Após isso configurado a gente vai fazer um commit no nosso repo para que a gente possa subir em um repo no GitHub e fazer o deploy em si. Nesse commit abaixo reparem que estou utilizando uma convenção famosa para commits que é o [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#specification) coloquem um comentário caso queiram que eu traga algo relacionado a padrões de commit :) ```bash git add . git commit -m "build(web): adding Netlify configuration file" ``` Já feito o commit agora precisamos criar um repo no GitHub para settarmos o remote e podermos dar push ![Imagem mostrando criação do repo no GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wyz9ih56t7zhxdobuse6.png) Aqui eu criei o repo com o nome `netlify-turborepo-post` mas vocês podem dar o nome que preferirem. Após isso vai abrir a tela do GitHub mostrando algo tipo assim ![Imagem mostrando o estado inicial do repo vazio no GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pf8qj76l9l08kxj7s3s9.png) Vocês vão copiar a parte de baixo já que já temos um repo existente ![Imagem mostrando o segundo quadrado do GitHub que devemos copiar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vonm2u0moerjgcg1uy29.png) E depois vocês vão colar no terminal do nosso projeto, rodar os comandos e voilá ✨, temos agora o nosso código dentro do GitHub ![Imagem mostrando nosso código dentro do GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fjyt2bbhy5f97wxg50s8.png) Bom... depois de ter criado e subido nosso código no GitHub, agora podemos ir pro Netlify fazer a última etapa 😁! ## Deployando site no Netlify Abram a conta de vocês no Netlify, depois clickem no `Add new site` ![Imagem mostrando uma seta apontando para o botão Add new site](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfpndqa3d619fh8wsmbs.png) Selecionem a opção `Import an existent project` ![Imagem mostrando selecionando a opção Import an existent project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02ryw39x9gvq2oqpvvgw.png) Selecionem o provider do GitHub ![Imagem mostrando uma seta apontando para o ícone do GitHub que é o provider que devemos selecionar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ygc2oh8gwrr7teau4zas.png) Pesquisem o nome que deram ao projeto de vocês ![Image mostrando a pesquisa para o nosso repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sn5y68ln56sni8phfijj.png) Depois coloquem essas configurações no **Basic build settings** ![Imagem mostrando que devemos colocar o base directory como apps/web, deixar o build command vazio e colocar o publish directory como apps/web/ .next](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72w3i8z16opeu4tt3mgr.png) Aí é só clicar em deploy e aguardar 😎... Após alguns minutos o site de vocês já vai estar deployado no Netlify lindamente 😁🎆. Porém ele vai ter essa cara feia aqui, masssssss... aí pra alterar isso é por conta de vocês! ![Imagem mostrando como ficou o site](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sl83phei0mqe45rywhu.png) Link do deploy: https://thayto-netlify-turborepo-post.netlify.app/ Link do repositório no GitHub: https://github.com/rafa-thayto/netlify-turborepo-post --- ### Como 'settar' a versão default do Node usando nvm Source: https://thayto.com/blog/como-settar-a-versao-default-do-node-usando-nvm --- title: "Como 'settar' a versão default do Node usando nvm" publishedTime: '2022-06-19T18:52:06Z' modifiedTime: '2022-10-25T23:52:06Z' description: 'Pra quem ainda não conhece, o `nvm` é um excelente utilitário pra quando estamos querendo trocar a versão do node com agilidade sem precisar desinstalar a atual e instalar a desejada.' image: { src: 'como-settar-a-versao-default-do-node-usando-nvm.png', placeholder: 'como-settar-a-versao-default-do-node-usando-nvm-small.png', type: 'image/png', } tags: ['node', 'nvm', 'tutorial'] href: '/blog/como-settar-a-versao-default-do-node-usando-nvm' reactionsLength: 0 commentsLength: 0 --- Pra quem ainda não conhece, o `nvm` é um excelente utilitário pra quando estamos querendo trocar a versão do node com agilidade sem precisar desinstalar a atual e instalar a desejada. Por um bom tempo eu utilizei o comando: ```bash nvm use ``` Para alternar a versão do node pra uma mais recente, até que uns dias atrás descobri como alterar de forma bem simples a versão default do nvm. Basta utilizar o seguinte comando alterando o trecho `` pela versão que você deseja manter como a padrão, por ex `16.5.0`: ```bash nvm alias default ``` E pronto, agora você vai estar sempre utilizando essa versão do nvm em qualquer momento ou terminal que você abrir 😁. --- ## Blog Posts (English) ### Quick Guide to Make Claude Code File Suggestion Faster Source: https://thayto.com/en/blog/claude-code-faster-file-suggestion --- title: 'Quick Guide to Make Claude Code File Suggestion Faster' publishedTime: '2026-01-08T12:00:00Z' modifiedTime: '2026-01-08T12:00:00Z' description: 'Learn how to improve Claude Code file suggestion with fzf for proper fuzzy matching, symlink support, and better performance.' image: { src: 'claude-code-faster-file-suggestion.jpg', placeholder: 'claude-code-faster-file-suggestion-small.jpg', type: 'image/jpeg', } tags: ['claude-code', 'cli', 'fzf', 'productivity', 'tutorial'] href: '/blog/claude-code-faster-file-suggestion' reactionsLength: 0 commentsLength: 0 --- The default `@` file matcher in Claude Code was always disappointing me, being used to superior options like fzf. It was also not suggesting files from gitignored or symlinked folders, which was annoying as hell. But Anthropic recently allowed us to customize it with File Suggestion settings. And oh boy! Proper fuzzy matching via fzf is so much better than what was there by default! ## Prerequisites You'll need these CLI tools installed: - **rg (ripgrep)** - for fast file listing with symlink support - **fzf** - for fuzzy matching - **jq** - for parsing JSON input ## Setup ### 1. Configure Claude Code Settings Add this to your `~/.claude/settings.json`: ```json "fileSuggestion": { "type": "command", "command": "~/.claude/file-suggestion.sh" } ``` ### 2. Create the File Suggestion Script Create `~/.claude/file-suggestion.sh` with the following content: ```bash #!/bin/bash # Custom file suggestion script for Claude Code # Uses rg + fzf for fuzzy matching and symlink support # Parse JSON input to get query QUERY=$(jq -r '.query // ""') # Use project dir from env, fallback to pwd PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}" # cd into project dir so rg outputs relative paths cd "$PROJECT_DIR" || exit 1 { # Main search - respects .gitignore, includes hidden files, follows symlinks rg --files --follow --hidden . 2>/dev/null # Additional paths - include even if gitignored (uncomment and customize) # [ -e .notes ] && rg --files --follow --hidden --no-ignore-vcs .notes 2>/dev/null } | sort -u | fzf --filter "$QUERY" | head -15 ``` ### 3. Make it Executable Don't forget to make the script executable: ```bash chmod +x ~/.claude/file-suggestion.sh ``` ### 4. Restart Claude Code Restart Claude Code and enjoy! Be happy :) --- ### Micro Frontends with Next.js Multi Zones Source: https://thayto.com/en/blog/microfrontends-next-js-multi-zones --- title: 'Micro Frontends with Next.js Multi Zones' publishedTime: '2024-10-01T16:09:33Z' modifiedTime: '2024-10-03T16:09:33Z' description: "When we're developing a Web application and it ends up scaling, we usually need a team. When that application scales even more, we need more teams working on the same application." image: { src: 'microfrontends-next-js-multi-zones.jpg', placeholder: 'microfrontends-next-js-multi-zones-small.jpg', type: 'image/jpeg', } tags: ['microfrontends', 'nextjs', 'tutorial', 'react'] href: '/blog/microfrontends-next-js-multi-zones' reactionsLength: 0 commentsLength: 0 --- ## Introduction When we're developing a Web application and it ends up scaling, we usually need a team. When that application scales even more, we need more teams working on the same application. Why am I talking about this? Because usually when we have many people changing the same project, we can have several problems. Whether it's many _Pull Requests_, differences in scopes, distinct business rules, _merge_ conflicts and even differences of opinion. When this happens what we usually think about is separating a piece of it into another project and creating the famous Micro Frontend that will speed up our deliveries and ensure the project evolves faster and independently of other scopes. Which makes development much easier! With that, the _Next.js_ team created an advanced feature called [**Multi Zones**](https://nextjs.org/docs/advanced-features/multi-zones), with it we can create several Micro Frontends very quickly and simply using _**Next.js**_ ## Prerequisites Before continuing to read this article, I assume you have basic knowledge / familiarity with **_Next.js_** ## What We're Going to Do By the end of this article we'll make 2 apps with Next.js, combine them using the **Multi Zones** feature. I'll also make a part 2 showing how to deploy the 2 applications on Vercel. ## Creating the Project Folder Open your terminal/cmd and run the following command: ```bash mkdir nextjs-multi-zones-post cd nextjs-multi-zones-post ``` Right after open that same folder in your preferred editor! (back then it was VSCode 💩, nowadays it's Neovim/VIM ❤️) ## Creating the Projects After entering the folder that will centralize our projects, it's time to create them! ### First Application - Home Open your terminal there and write the following command to create the first project which will be our **HOME**: ```bash npx create-next-app --ts home cd home ``` After creating the home, let's change the port that the project will run on to make our lives easier and have a standard until the end of the post Open the `package.json` file inside your home and change the dev script from: ```json ... "scripts": { "dev": "next dev", ... }, ... ``` to: ```json ... "scripts": { "dev": "next dev -p 4444", ... }, ... ``` Open your terminal in the same folder and run the following command to execute the application: ``` npm run dev ``` ### Second Application - Blog Open another terminal and run the following command to create our second project which will be our **BLOG**: ```bash npx create-next-app --ts blog cd blog ``` We'll follow the same procedure we did for home now with the blog Open the `package.json` file inside your blog and change the dev script from: ```json ... "scripts": { "dev": "next dev", ... }, ... ``` to: ```json ... "scripts": { "dev": "next dev -p 7777", ... }, ... ``` Open your terminal in the same folder and run the following command to execute the application: ``` npm run dev ``` Done, now we have the 2 applications running and the result should be something like this: ![Expected result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/iemso2388vls7qftr0b9.png) **Home (Application 1)** running on port **http://localhost:4444** and the **Blog (Application 2)** running on port **http://localhost:7777** ## Configuring Home (Application 1) The first project we're going to develop and configure will be Home which besides being the base project will also be our Home. It's in it where we'll configure the URLs that we'll [rewrite](https://nextjs.org/docs/api-reference/next.config.js/rewrites) so that the Micro Frontend and Multi Zones work. Open the `next.config.js` file and add the following lines of code: ```js const { BLOG_URL } = process.env const nextConfig = { async rewrites() { return [ { source: '/:path*', destination: `/:path*`, }, { source: '/blog', destination: `${BLOG_URL}/blog`, }, { source: '/blog/:path*', destination: `${BLOG_URL}/blog/:path*`, }, ] }, } ``` In the `rewrites` part it's basically a rewrite, we'll rewrite the `/blog` routes and everything that comes after, like `/blog/posts/hello-world`. To learn more about rewrites just access the official **Next.js** documentation. And you can see that we also added a constant using destructuring on the `process.env` object to get the blog `URL` via environment variables. Now let's create the `.env` file at the root of our `home` project as shown in the example below. ![Where the .env file will be located](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e4ky9svj4jt4txy51zh8.png) It's in it where we'll put the `BLOG_URL` pointing to `http://localhost:7777` which is where our blog project is running. `.env` ```txt BLOG_URL=http://localhost:7777 ``` Now inside our home project we'll transform the `index.tsx` into this: ```tsx import type { NextPage } from 'next' import Head from 'next/head' import Image from 'next/image' import Link from 'next/link' import styles from '../styles/Home.module.css' const Home: NextPage = () => { return ( ) } export default Home ``` Basically we added 3 links, 2 of them to pages of our blog (not yet built) and 1 link to redirect to an internal page of ours which is _About Us_. ![Home page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5w3xa558sm1z0c5qmdf5.png) Now let's create the `about.tsx` file inside `home/pages/about.tsx`: ![Where our about.tsx file will be located](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7701xk00103lwhr83k8t.png) `about.tsx` ```tsx import type { NextPage } from 'next' import Link from 'next/link' const About: NextPage = () => { return (

About us :O


) } export default About ``` Here we created a very simple about us page that just has some text and a link that takes us back home. And so we finish the **Home** part. Now let's go to Blog! ## Configuring Blog (Application 2) Now we'll configure the blog. In the blog it'll be a bit simpler, basically we'll open the `next.config.js` file and add the `basePath` property inside `nextConfig`, it will tell next that its base _URL_ will start at the address we choose, which in this case will be `/blog` `next.config.js` ```js const nextConfig = { basePath: '/blog', } module.exports = nextConfig ``` > Remember that every time we change the **next.config.js** file or add something to **.env** we need to stop the server and start it again 😁 Now inside our blog project we'll transform the `index.tsx` into this: ```tsx import type { NextPage } from 'next' import Head from 'next/head' import Image from 'next/image' import Link from 'next/link' import styles from '../styles/Home.module.css' const Blog: NextPage = () => { return ( ) } export default Blog ``` At the root of our blog we have 1 link that leads to our **Home** and 2 links that lead us to posts in our blog ![Blog page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/buxvf0bmqx9e3mnj4erj.png) In the image above you can already see the magic happening, if you look at the URL you'll notice we're running on **http://localhost:4444/blog**, but why does this happen? When we configured the `basePath` inside the **Blog** project's `next.config.js`, we tell next that `index.tsx` and all other files will always be accessed from our `basePath` which in this case is `/blog`. We should also remember that in our **Home** project's `next.config.js` we configured that whenever we're on **http://localhost:4444** and access `/blog` or anything after blog, we'll be doing a route rewrite and pointing to our **Blog** via **Home**. > In other words: http://localhost:4444/blog will point to http://localhost:7777/blog The most amazing thing about all this is that we can access our other application (blog) inside our application (home) **without changing the URL** 🤯, isn't that impressive? _Cover Image: Photo by Andrey Tikhonovskiy on Unsplash_ --- ### Zero Downtime Deployment with Docker Compose in an OCI VPS using GitHub Actions Source: https://thayto.com/en/blog/zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions --- title: 'Zero Downtime Deployment with Docker Compose in an OCI VPS using GitHub Actions' publishedTime: '2024-09-13T21:16:36Z' modifiedTime: '2024-09-13T21:16:36Z' description: 'First, you must have a Linux VPS (Ill use Ubuntu 22 in this tutorial). Tagged with docker, devops, github actions, linux, tutorial.' image: { src: 'zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions.jpg', placeholder: 'zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions-small.jpg', type: 'image/jpeg', } tags: ['docker', 'devops', 'github actions', 'linux', 'tutorial'] href: '/blog/zero-downtime-deployment-with-docker-compose-in-an-oci-vps-using-github-actions' reactionsLength: 0 commentsLength: 0 --- First, you must have a Linux VPS (I'll use Ubuntu 22 in this tutorial). ## Requirements - Docker - Docker Compose - [Docker Rollout Plugin](https://github.com/Wowu/docker-rollout?tab=readme-ov-file#installation) ### Installing Docker Rollout Plugin It's very simple: ```bash # Create directory for Docker cli plugins mkdir -p ~/.docker/cli-plugins # Download docker-rollout script to Docker cli plugins directory curl https://raw.githubusercontent.com/wowu/docker-rollout/master/docker-rollout -o ~/.docker/cli-plugins/docker-rollout # Make the script executable chmod +x ~/.docker/cli-plugins/docker-rollout ``` ### Docker Rollout Usage Run `docker rollout ` instead of `docker compose up -d ` to update a service without downtime. ```bash $ docker rollout -f docker-compose.yml ``` ## GitHub Actions build_and_deploy.yml workflow > In this case I'm using a private registry on Digital Ocean. ```yaml name: CI # 1 # Controls when the workflow will run on: # Triggers the workflow on push events but only for the master branch push: branches: [main] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: inputs: version: description: 'Image version' required: true #2 env: REGISTRY: 'registry.digitalocean.com/YOUR_REGISTRY_NAME' IMAGE_NAME: 'YOUR_IMAGE_NAME' #3 jobs: build_and_push: runs-on: ubuntu-latest steps: - name: Checkout the repo uses: actions/checkout@v4 - name: Build container image run: docker build -t $(echo $REGISTRY)/$(echo $IMAGE_NAME):$(echo $GITHUB_SHA | head -c7) . - name: Install doctl uses: digitalocean/action-doctl@v2 with: token: ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} - name: Log in to DigitalOcean Container Registry with short-lived credentials run: doctl registry login --expiry-seconds 600 - name: Remove all old images run: if [ ! -z "$(doctl registry repository list | grep "$(echo $IMAGE_NAME)")" ]; then doctl registry repository delete-manifest $(echo $IMAGE_NAME) $(doctl registry repository list-tags $(echo $IMAGE_NAME) | grep -o "sha.*") --force; else echo "No repository"; fi - name: Push image to DigitalOcean Container Registry run: docker push $(echo $REGISTRY)/$(echo $IMAGE_NAME):$(echo $GITHUB_SHA | head -c7) deploy: runs-on: ubuntu-latest needs: build_and_push steps: - name: Deploy to Digital Ocean droplet via SSH action uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.OCI_HOST }} username: ${{ secrets.OCI_USERNAME }} key: ${{ secrets.OCI_SSH_KEY }} envs: IMAGE_NAME,REGISTRY,{{ secrets.DIGITALOCEAN_ACCESS_TOKEN }},GITHUB_SHA script: | whoami cd ~/app pwd ls -la # Login to registry docker login -u ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} -p ${{ secrets.DIGITALOCEAN_ACCESS_TOKEN }} registry.digitalocean.com # Update the APP image in docker-compose.yml sed -i '0,/image:/s|image: .*|image: '$(echo $REGISTRY)'/'$(echo $IMAGE_NAME)':'$(echo $GITHUB_SHA | head -c7)'|' docker-compose.yml # Pull the latest image docker compose pull # Here is the magic docker plugin docker rollout -f docker-compose.yml app echo "Deployed with success to production" ``` ## Conclusion I hope this tutorial helps you in some way, best regards Rafael Thayto. _Photo by Bernd 📷 Dittrich on Unsplash_ --- ### Exposing Your Localhost with Ngrok (Very Useful for Testing Webhooks) Source: https://thayto.com/en/blog/expondo-seu-localhost-com-ngrok --- title: 'Exposing Your Localhost with Ngrok (Very Useful for Testing Webhooks)' publishedTime: '2024-07-06T15:58:51Z' modifiedTime: '2024-07-06T15:58:51Z' description: 'I needed to test some webhooks, but only knew one way which would be to deploy the app and only then be able to test the webhooks. This method works and is widely used by devs. But it has a problem... every test you need to deploy your application again and that takes quite some time.' image: { src: 'expondo-seu-localhost-com-ngrok.jpg', placeholder: 'expondo-seu-localhost-com-ngrok-small.jpg', type: 'image/jpeg', } tags: ['ngrok', 'webhooks', 'tutorial'] href: '/blog/expondo-seu-localhost-com-ngrok' reactionsLength: 0 commentsLength: 0 --- > The "it works on my machine" problems are over! ## Problem I needed to test some webhooks, but only knew one way which would be to deploy the app and only then be able to test the webhooks. This method works and is widely used by devs. But it has a problem... every test you need to deploy your application again and that takes quite some time. Searching around I found [Ngrok](https://ngrok.com/) and with it we can expose our localhost in a very simple way. ## Installation > you can also follow the [Official Documentation](https://ngrok.com/docs/guides/getting-started/#step-2-install-the-ngrok-agent). ### Mac Use [Homebrew](https://brew.sh/): ```bash brew install ngrok/ngrok/ngrok ``` ### Linux Use apt ```bash curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | \ sudo gpg --dearmor -o /etc/apt/keyrings/ngrok.gpg && \ echo "deb [signed-by=/etc/apt/keyrings/ngrok.gpg] https://ngrok-agent.s3.amazonaws.com buster main" | \ sudo tee /etc/apt/sources.list.d/ngrok.list && \ sudo apt update && sudo apt install ngrok ``` ### Windows Use [Chocolatey](https://chocolatey.org/install) ```bash choco install ngrok ``` ## Connect Your Agent to Your Ngrok Account To do this, you need to [log in or sign up](https://dashboard.ngrok.com/) on Ngrok and get your Authtoken. Copy the token and add it to your terminal ```bash ngrok config add-authtoken ``` ## Run Ngrok Start Ngrok by running the command below (you can choose the port you prefer, but in the example I used port `8000`): ```bash ngrok http 8000 ``` Thanks for reading this far! Hope this post helps you :) Check out more at https://thayto.com/links _Cover Image: Photo by Stepan Kalinin on Unsplash_ --- ### TypeScript - How to Extract a String from Your Literal Types Source: https://thayto.com/en/blog/ts-como-pegar-string-no-seu-literal-types --- title: 'TypeScript - How to Extract a String from Your Literal Types' publishedTime: '2023-07-24T15:58:51Z' modifiedTime: '2024-07-07T21:33:51Z' description: "I won't beat around the bush. Basically just use TypeScript's Extract Utility Type and that's it!" image: { src: 'ts-como-pegar-string-no-seu-literal-types.jpg', placeholder: 'ts-como-pegar-string-no-seu-literal-types-small.jpg', type: 'image/jpeg', } tags: ['typescript', 'utilities', 'types', 'tutorial'] href: '/blog/ts-como-pegar-string-no-seu-literal-types' reactionsLength: 0 commentsLength: 0 --- Hey there, senior devs. I won't beat around the bush. Basically just use **TypeScript**'s _Utility Type_ `Extract` and that's it! _**For example:**_ ```ts type CatNames = 'Garfield' | 'Yoru' | 'Yuki' | 'Fluffy' | 'Lady' | 'Lucky' type MyCatNames = Extract // |-> Extract these two `literal types` from CatNames `literal type` type Cat = { age: number name: CatNames } function getMyCat(age: number, name: MyCatNames): Cat { // |--> only show 'Yoru' and // 'Yuki' return { age, name, } as Cat } const myCat1 = getMyCat(1, 'Yoru') const myCat2 = getMyCat(1, 'Yuki') const otherCat: Cat = { age: 3, name: 'Garfield', } const otherCatWithMyCatName: Cat = { age: 5, name: 'Yoru', } // cheers! ``` > [TS Playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBAwghsAcnAthAzlAvFA5AcTgCcAzASwgBsATXKAHzwE0B7IgVzsdyfYGsyXPADFK7EiRBDcAGTjUpDPDPYBjPlIBQm0JCgBZEPCSoM2KAFEAHsCJxVwADzHkadABpmbTkp79BAHyaUCGhYeERkVEA9NEMALQBljZ2DlDAABYY0MAA7ixQAAaUZMAQdpTp4BiFUCRELCiwCK5mxaXlcJW6EIXaPc3A5gDewVBwAOYQAFxQAHbsKABG5e5jc6azLqbomgC+2iTscw5kLHNQU8CGxgAUkzPziytEnhtoszctOwCUWwhQUZRYEgsKxBKJKDnSggKDoDIsXJeDh0OBzahQMag7ERcE4kJ+AS4MZECDAdhEC5A0IPNZhd4QOlQPbjTDGfbaVTndBDFBGBAARnMVy+wFuAs8PG8uB+mi5cx5UD5xgATMKyaLxZLeETZXLuUMWJlysZ-kMcNSQg9ZgBmJkhBmzAjEchUWgc+WKo1ZIjGADqpQyotaZpGWPGU1mAFZ7fNNsjOPsgA) link hope this helps someone! (NOTE: this post was written in 10 minutes max) my links: https://thayto.com/linktree _Photo by Karina Vorozheeva on Unsplash_ --- ### TypeScript - Template Literal Types Source: https://thayto.com/en/blog/typescript-template-literal-types --- title: 'TypeScript - Template Literal Types' publishedTime: '2023-07-07T07:30:51Z' modifiedTime: '2024-07-07T21:08:51Z' description: 'Template Literal Types is really cool and expands the possibilities of what was already possible with string literals + unions in TS, check it out! It has the same syntax as JavaScript template literal strings.' image: { src: 'typescript-template-literal-types.png', placeholder: 'typescript-template-literal-types-small.png', type: 'image/png', } tags: ['typescript', 'utilities', 'types', 'learning'] href: '/blog/typescript-template-literal-types' reactionsLength: 0 commentsLength: 0 --- ## What is Template Literal Types? **Template Literal Types** is really cool and expands the possibilities of what was already possible with `string literals + unions` in TS, check it out! It has the same syntax as JavaScript template literal strings. ```ts type Goodbye = 'goodbye!' type Greeting = `Hi ${Goodbye}` ``` With it, you can use your needs + imagination to do several cool things that will add a lot to your project/type. A nice example would be to standardize that all extra properties we add to a type need to have the prefix "data\_" and the suffix "\_prop", so we can create an object with a pattern that will make it easier to read and know which extra properties were sent. Talk is cheap, but where's the example? Let's start by creating an EventData type, it's our base for our event object. ```ts type EventData = { id: string name: string step: 'first_step' | 'second_step' } ``` It already has defined properties that cannot be changed, but now we need to send some more fields related to the fields it has in each step. We could extend the EventData type and add the fields, but instead, let's use Template Literal Types! ```ts type FormFields = { name: string age: number } type EventDataExtraProps = `field_${string & keyof T}` // here we'll modify EventData a bit type EventData = { id: string name: string step: 'first_step' | 'second_step' } & Record, any> const event: EventData = { id: 'sextou?', name: 'thayto', step: 'first_step', field_name: 'full_name', field_age: 22, } ``` It seems complex, but it's quite simple. In large projects / libs, with a single utility like this, we can work magic on auto_completes and standardize sending certain properties! Hope you learned something, if not, at least you read until the end, cheers! https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html ![image calling you dumb if you still don't know template literal types](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/12fb3fct25e3hcwbhr9d.png) --- ### How to Open Visual Studio Code (VSCode) Inside GitHub Source: https://thayto.com/en/blog/como-abrir-o-vscode-direto-do-github --- title: 'How to Open Visual Studio Code (VSCode) Inside GitHub' publishedTime: '2022-11-05T15:16:36Z' modifiedTime: '2024-07-07T20:43:36Z' description: 'Every developer at some point has needed to edit a file inside GitHub and encountered this standard GitHub editing screen' image: { src: 'como-abrir-o-vscode-direto-do-github.png', placeholder: 'como-abrir-o-vscode-direto-do-github-small.png', type: 'image/png', } tags: ['github', 'tutorial', 'vscode'] href: '/blog/como-abrir-o-vscode-direto-do-github' reactionsLength: 12 commentsLength: 0 --- ## Quick way: Just press the `.` key inside your repository of choice > at the end of the post there are some other ways to open github.dev too! ## TL;DR Every developer at some point has needed to edit a file inside GitHub and encountered this standard GitHub editing screen: ![GitHub standard editing screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i3kximxr5ck48anftm7b.png) At first it seems like an "OK" screen for making a quick edit, but when we need to search for more things inside the repository, this screen doesn't help us much and that's why I'm going to show you how to open Visual Studio Code (VSCode) directly from GitHub. It's pretty simple, just press the `.` **(period)** key on your keyboard and voila, it will magically open a VSCode directly in your browser and inside your repository with almost all the features that VSCode has. ![VSCode loading screen inside the browser](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p3fkli1ddemzshin31m6.png) ![VSCode open inside the browser showing the repository](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oaiou6k7vvaufog1rsgh.png) Now you can do as many searches as you want, work on as many files as necessary, and you'll also be able to make commits, install extensions, change the theme, among many other features. ## Other Ways to Open VSCode Directly from GitHub Other ways to open any GitHub repo in `github.dev` (GitHub's VSCode) are: - Changing the URL from `github.com` to `github.dev` - Pressing the `.` key inside any repository - When opening a file inside any repository, select the dropdown menu ▼ and click on open with `github.dev`. ![Screenshot of github screen showing the dropdown menu and open with button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jinx6wgkatgmocwaubzb.png) That was today's tip, hope it helped, cheers! 😎 --- ### Difference Between the ?, ?? and || Operators in JavaScript / TypeScript Source: https://thayto.com/en/blog/diferenca-entre-operadores-js-ts --- title: 'Difference Between the ?, ?? and || Operators in JavaScript / TypeScript' publishedTime: '2022-10-31T19:34:48Z' modifiedTime: '2024-07-11T19:34:53Z' description: '? -> Optional Chaining Operator. Allows reading the value of a property located internally in a chain of connected objects without having to explicitly validate each reference in the chain.' image: { src: 'diferenca-entre-operadores-js-ts.png', placeholder: 'diferenca-entre-operadores-js-ts-small.png', type: 'image/png', } tags: ['javascript', 'typescript', 'learning'] href: '/blog/diferenca-entre-operadores-js-ts' reactionsLength: 0 commentsLength: 0 --- # Context I believe every developer has had doubts about the differences between some JavaScript/TypeScript operators. That's why I made this quick and simple post about the differences between the ?, ?? and || operators. Check it out below ## ? - Optional Chaining `?` -> **Optional Chaining Operator**. Allows reading the value of a property located internally in a chain of connected objects without having to explicitly validate each reference in the chain. The `?.` operator works similarly to the `.` chaining operator, except that instead of causing an error if a reference is [nullish](https://developer.mozilla.org/en-US/docs/Glossary/Nullish) (`null` or `undefined`), the expression short-circuits and returns with a value of `undefined`. When used with a function call, it returns `undefined` if the executed function does not exist. This results in shorter and simpler expressions when accessing chained properties when there's a possibility of a reference being non-existent. This can also help when exploring the content of an object when there's no guarantee of certain required properties existing. **Example** ```js const user = { id: 13, name: 'Thayto', } console.log(user?.name) // John console.log(user?.fullName) // undefined, application won't break. console.log(user.fullName) // TypeError: Cannot read property 'fullName' of undefined, application breaks. ``` ## ?? - Nullish Coalescing `??` -> **Nullish Coalescing Operator**. Is a logical operator that returns its right-hand side operand when its left-hand side operand is `null` or `undefined`. Otherwise, it returns its left-hand side operand. **Example** ```js console.log(13 ?? 'not found') // 13 console.log(0 ?? 'not found') // 0 console.log('Dri' ?? 'not found') // "Dri" console.log('' ?? 'not found') // "" console.log(true ?? 'not found') // true console.log(false ?? 'not found') // false console.log(undefined ?? 'not found') // "not found" console.log(null ?? 'not found') // "not found" ``` ## || - Logical OR `||` is the **Logical OR Operator** The Logical OR expression is evaluated from left to right, it is tested for possible "short-circuit" evaluation using the following rule: `(some truthy expression) || expr` is short-circuit evaluated to the truthy expression. Short-circuit means that the `expr` part above is not evaluated, therefore any side effects of doing so do not take effect (for example, if `expr` is a function call, the call never occurs). This happens because the value of the operator is already determined after evaluation of the first operand. **Example** ```js console.log(13 || 'not found') // 13 console.log(0 || 'not found') // "not found" console.log('Dri' || 'not found') // "Dri" console.log('' || 'not found') // "not found" console.log(true || 'not found') // true console.log(false || 'not found') // "not found" console.log(undefined || 'not found') // "not found" console.log(null || 'not found') // "not found" ``` --- ### Supercharging Your Git Bash (Windows) by Adding Aliases (Like the zsh git plugin) Source: https://thayto.com/en/blog/turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh --- title: 'Supercharging Your Git Bash (Windows) by Adding Aliases (Like the zsh git plugin)' publishedTime: '2022-10-23T18:55:46Z' modifiedTime: '2022-10-23T18:55:46Z' description: "In this simple guide I'll show you how to supercharge your git bash with aliases that will make it much easier and faster when making your commits, pulls, getting logs, switching branches, rebases, merges, etc." image: { src: 'turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh.jpg', placeholder: 'turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh-small.jpg', type: 'image/jpeg', } tags: ['git', 'bash', 'tutorial', 'windows'] href: '/blog/turbinando-seu-git-bash-windows-adicionando-aliases-como-se-fosse-o-git-plugin-do-zsh' reactionsLength: 0 commentsLength: 0 --- In this simple guide I'll show you how to supercharge your git bash with aliases that will make it much easier and faster when making your commits, pulls, getting logs, switching branches, rebases, merges, etc.. > First, for those who don't know, the famous zsh git plugin is this one: [zsh git plugin](https://github.com/ohmyzsh/ohmyzsh/blob/master/plugins/git/git.plugin.zsh) Well, the first step to add these aliases is to have [git bash](https://git-scm.com/download/win) installed. And to add the aliases is quite simple, just access the folder where the git bash profiles are (remembering that this guide is only for **Windows**), which will be at the following path: `C:\Program Files\Git\etc\profile.d` ![Selecting the aliases file](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u76fyz7yqeztqzk54erg.png) Then you'll look for the `aliases.sh` file and open it in your preferred text editor (can even be notepad) and add the following lines: ```bash alias g=git alias ga='git add' alias gaa='git add --all' alias gam='git am' alias gama='git am --abort' alias gamc='git am --continue' alias gams='git am --skip' alias gamscp='git am --show-current-patch' alias gap='git apply' alias gapa='git add --patch' alias gapt='git apply --3way' alias gau='git add --update' alias gav='git add --verbose' alias gb='git branch' alias gbD='git branch -D' alias gba='git branch -a' alias gbd='git branch -d' alias gbda='git branch --no-color --merged | command grep -vE "^([+*]|\s*($(git_main_branch)|$(git_develop_branch))\s*$)" | command xargs git branch -d 2>/dev/null' alias gbl='git blame -b -w' alias gbnm='git branch --no-merged' alias gbr='git branch --remote' alias gbs='git bisect' alias gbsb='git bisect bad' alias gbsg='git bisect good' alias gbsr='git bisect reset' alias gbss='git bisect start' alias gc='git commit -v' alias 'gc!'='git commit -v --amend' alias gca='git commit -v -a' alias 'gca!'='git commit -v -a --amend' alias gcam='git commit -a -m' alias 'gcan!'='git commit -v -a --no-edit --amend' alias 'gcans!'='git commit -v -a -s --no-edit --amend' alias gcas='git commit -a -s' alias gcasm='git commit -a -s -m' alias gcb='git checkout -b' alias gcd='git checkout $(git_develop_branch)' alias gcf='git config --list' alias gcl='git clone --recurse-submodules' alias gclean='git clean -id' alias gcm='git checkout $(git_main_branch)' alias gcmsg='git commit -m' alias 'gcn!'='git commit -v --no-edit --amend' alias gco='git checkout' alias gcor='git checkout --recurse-submodules' alias gcount='git shortlog -sn' alias gcp='git cherry-pick' alias gcpa='git cherry-pick --abort' alias gcpc='git cherry-pick --continue' alias gcs='git commit -S' alias gcsm='git commit -s -m' alias gcss='git commit -S -s' alias gcssm='git commit -S -s -m' alias gd='git diff' alias gdca='git diff --cached' alias gdct='git describe --tags $(git rev-list --tags --max-count=1)' alias gdcw='git diff --cached --word-diff' alias gds='git diff --staged' alias gdt='git diff-tree --no-commit-id --name-only -r' alias gdup='git diff @{upstream}' alias gdw='git diff --word-diff' alias gf='git fetch' alias gfa='git fetch --all --prune --jobs=10' alias gfg='git ls-files | grep' alias gfo='git fetch origin' alias gg='git gui citool' alias gga='git gui citool --amend' alias ggpull='git pull origin "$(git_current_branch)"' alias ggpur=ggu alias ggpush='git push origin "$(git_current_branch)"' alias ggsup='git branch --set-upstream-to=origin/$(git_current_branch)' alias ghh='git help' alias gignore='git update-index --assume-unchanged' alias gignored='git ls-files -v | grep "^[[:lower:]]"' alias git-svn-dcommit-push='git svn dcommit && git push github $(git_main_branch):svntrunk' alias gk='\gitk --all --branches &!' alias gke='\gitk --all $(git log -g --pretty=%h) &!' alias gl='git pull' alias glg='git log --stat' alias glgg='git log --graph' alias glgga='git log --graph --decorate --all' alias glgm='git log --graph --max-count=10' alias glgp='git log --stat -p' alias glo='git log --oneline --decorate' alias globurl='noglob urlglobber ' alias glod='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ad) %C(bold blue)<%an>%Creset'\' alias glods='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ad) %C(bold blue)<%an>%Creset'\'' --date=short' alias glog='git log --oneline --decorate --graph' alias gloga='git log --oneline --decorate --graph --all' alias glol='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ar) %C(bold blue)<%an>%Creset'\' alias glola='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ar) %C(bold blue)<%an>%Creset'\'' --all' alias glols='git log --graph --pretty='\''%Cred%h%Creset -%C(auto)%d%Creset %s %Cgreen(%ar) %C(bold blue)<%an>%Creset'\'' --stat' alias glp=_git_log_prettily alias gluc='git pull upstream $(git_current_branch)' alias glum='git pull upstream $(git_main_branch)' alias gm='git merge' alias gma='git merge --abort' alias gmom='git merge origin/$(git_main_branch)' alias gmtl='git mergetool --no-prompt' alias gmtlvim='git mergetool --no-prompt --tool=vimdiff' alias gmum='git merge upstream/$(git_main_branch)' alias gp='git push' alias gpd='git push --dry-run' alias gpf='git push --force-with-lease' alias 'gpf!'='git push --force' alias gpoat='git push origin --all && git push origin --tags' alias gpr='git pull --rebase' alias gpristine='git reset --hard && git clean -dffx' alias gpsup='git push --set-upstream origin $(git_current_branch)' alias gpu='git push upstream' alias gpv='git push -v' alias gr='git remote' alias gra='git remote add' alias grb='git rebase' alias grba='git rebase --abort' alias grbc='git rebase --continue' alias grbd='git rebase $(git_develop_branch)' alias grbi='git rebase -i' alias grbm='git rebase $(git_main_branch)' alias grbo='git rebase --onto' alias grbom='git rebase origin/$(git_main_branch)' alias grbs='git rebase --skip' alias grep='grep --color=auto --exclude-dir={.bzr,CVS,.git,.hg,.svn,.idea,.tox}' alias grev='git revert' alias grh='git reset' alias grhh='git reset --hard' alias grm='git rm' alias grmc='git rm --cached' alias grmv='git remote rename' alias groh='git reset origin/$(git_current_branch) --hard' alias grrm='git remote remove' alias grs='git restore' alias grset='git remote set-url' alias grss='git restore --source' alias grst='git restore --staged' alias grt='cd "$(git rev-parse --show-toplevel || echo .)"' alias gru='git reset --' alias grup='git remote update' alias grv='git remote -v' alias gsb='git status -sb' alias gsd='git svn dcommit' alias gsh='git show' alias gsi='git submodule init' alias gsps='git show --pretty=short --show-signature' alias gsr='git svn rebase' alias gss='git status -s' alias gst='git status' alias gsta='git stash push' alias gstaa='git stash apply' alias gstall='git stash --all' alias gstc='git stash clear' alias gstd='git stash drop' alias gstl='git stash list' alias gstp='git stash pop' alias gsts='git stash show --text' alias gstu='gsta --include-untracked' alias gsu='git submodule update' alias gsw='git switch' alias gswc='git switch -c' alias gswd='git switch $(git_develop_branch)' alias gswm='git switch $(git_main_branch)' alias gtl='gtl(){ git tag --sort=-v:refname -n -l "${1}*" }; noglob gtl' alias gts='git tag -s' alias gtv='git tag | sort -V' alias gunignore='git update-index --no-assume-unchanged' alias gunwip='git log -n 1 | grep -q -c "\-\-wip\-\-" && git reset HEAD~1' alias gup='git pull --rebase' alias gupa='git pull --rebase --autostash' alias gupav='git pull --rebase --autostash -v' alias gupom='git pull --rebase origin $(git_main_branch)' alias gupomi='git pull --rebase=interactive origin $(git_main_branch)' alias gupv='git pull --rebase -v' alias gwch='git whatchanged -p --abbrev-commit --pretty=medium' alias gwip='git add -A; git rm $(git ls-files --deleted) 2> /dev/null; git commit --no-verify --no-gpg-sign -m "--wip-- [skip ci]"' ``` Done! Now you can be happier using git more quickly and writing much less with these aliases! :D --- ### How to Configure Turborepo Deployment on Netlify Source: https://thayto.com/en/blog/como-configurar-o-deploy-do-turborepo-no-netlify --- title: 'How to Configure Turborepo Deployment on Netlify' publishedTime: '2022-06-21T15:14:48Z' modifiedTime: '2022-10-25T23:50:06Z' description: "The other day I faced a challenge deploying one of my applications using Turborepo with Next.js and since I believe more people might have this problem, I decided to make a guide on how to do the initial configurations, let's go?" image: { src: 'como-configurar-o-deploy-do-turborepo-no-netlify.png', placeholder: 'como-configurar-o-deploy-do-turborepo-no-netlify-small.png', type: 'image/png', } tags: ['turborepo', 'netlify', 'tutorial', 'react', 'next.js'] href: '/blog/como-configurar-o-deploy-do-turborepo-no-netlify' reactionsLength: 0 commentsLength: 0 --- The other day I faced a challenge deploying one of my applications using Turborepo with Next.js and since I believe more people might have this problem, I decided to make a guide on how to do the initial configurations, let's go? ## Setup / Initial Configurations Well first let's start with the basics, create an application using Turborepo, which is quite simple, just execute this command below in the terminal :) ```bash npx create-turbo@latest ``` And then something like this will probably appear: ```bash Need to install the following packages: create-turbo@1.2.16 Ok to proceed? (y) ``` Just press `enter ⏎` or `y` and it will download this dependency so you can create the project in a much simpler way. Right after you'll see something like this: ```bash >>> TURBOREPO >>> Welcome to Turborepo! Let's get you set up with a new codebase. ? Where would you like to create your turborepo? (./my-turborepo) ``` And in this case I pressed `enter ⏎` to use the default name. Then it will ask you to use your preferred package manager: ```bash ? Which package manager do you want to use? (Use arrow keys) ❯ npm pnpm yarn ``` I chose `npm` to make things easier for you :D After all this you'll probably have something like this on your screen: ```bash >>> TURBOREPO >>> Welcome to Turborepo! Let's get you set up with a new codebase. ? Where would you like to create your turborepo? ./my-turborepo ? Which package manager do you want to use? npm >>> Creating a new turborepo with the following: - apps/web: Next.js with TypeScript - apps/docs: Next.js with TypeScript - packages/ui: Shared React component library - packages/eslint-config-custom: Shared configuration (ESLint) - packages/tsconfig: Shared TypeScript `tsconfig.json` > Installing dependencies... ``` And finally everything will be beautifully installed and we can move on to the interesting part. 😎. Let's open the folder with our preferred code editor (in my case it's VSCode) and when we open it we'll have something like this in the folder structure: ![Image showing how the project structure should look](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fizlxcl9y35xg8ixxxld.png) ## Creating Netlify Configuration File After verifying that everything is correct, let's create a file called `netlify.toml` inside `./apps/web` ![Image showing where the file we created should be located](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwu7606izt80mx6u6bwx.png) And inside `netlify.toml` we'll put the following code: ```toml [build] command = "cd ../.. && npm install && npm run build" publish = ".next" [[plugins]] package = "@netlify/plugin-nextjs" ``` Okay, but what's this for? Well, this is basically the configuration file that Netlify looks at before deploying. The `[build]` is the step that Netlify looks at to know how to build The `[[plugins]]` is the step where before building it installs some plugins that will make deployment easier internally. In our case it's extremely important to add the `Next.js` plugin to avoid any complications when it creates the internal cache and uses Next's `SSR` and `Edge Functions`. After this is configured we'll make a commit to our repo so we can push it to a GitHub repo and do the actual deployment. In the commit below notice that I'm using a famous convention for commits which is [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#specification) leave a comment if you want me to bring something related to commit standards :) ```bash git add . git commit -m "build(web): adding Netlify configuration file" ``` Once the commit is done we now need to create a repo on GitHub to set the remote and be able to push ![Image showing repo creation on GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wyz9ih56t7zhxdobuse6.png) Here I created the repo with the name `netlify-turborepo-post` but you can give it whatever name you prefer. After that the GitHub screen will open showing something like this ![Image showing the initial state of the empty repo on GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pf8qj76l9l08kxj7s3s9.png) You'll copy the bottom part since we already have an existing repo ![Image showing the second GitHub box that we should copy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vonm2u0moerjgcg1uy29.png) And then you'll paste it in our project's terminal, run the commands and voila ✨, we now have our code inside GitHub ![Image showing our code inside GitHub](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fjyt2bbhy5f97wxg50s8.png) Well... after creating and pushing our code to GitHub, we can now go to Netlify for the final step 😁! ## Deploying Site on Netlify Open your Netlify account, then click on `Add new site` ![Image showing an arrow pointing to the Add new site button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xfpndqa3d619fh8wsmbs.png) Select the `Import an existent project` option ![Image showing selecting the Import an existent project option](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/02ryw39x9gvq2oqpvvgw.png) Select the GitHub provider ![Image showing an arrow pointing to the GitHub icon which is the provider we should select](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ygc2oh8gwrr7teau4zas.png) Search for the name you gave your project ![Image showing the search for our repo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sn5y68ln56sni8phfijj.png) Then put these settings in the **Basic build settings** ![Image showing that we should set base directory as apps/web, leave build command empty and set publish directory as apps/web/ .next](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/72w3i8z16opeu4tt3mgr.png) Then just click deploy and wait 😎... After a few minutes your site will be beautifully deployed on Netlify 😁🎆. However it will look ugly like this, buttttt... changing that is up to you! ![Image showing how the site turned out](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sl83phei0mqe45rywhu.png) Deploy link: https://thayto-netlify-turborepo-post.netlify.app/ GitHub repository link: https://github.com/rafa-thayto/netlify-turborepo-post --- ### How to Set the Default Node Version Using nvm Source: https://thayto.com/en/blog/como-settar-a-versao-default-do-node-usando-nvm --- title: 'How to Set the Default Node Version Using nvm' publishedTime: '2022-06-19T18:52:06Z' modifiedTime: '2022-10-25T23:52:06Z' description: "For those who don't know yet, `nvm` is an excellent utility for when we want to switch Node versions quickly without having to uninstall the current one and install the desired one." image: { src: 'como-settar-a-versao-default-do-node-usando-nvm.png', placeholder: 'como-settar-a-versao-default-do-node-usando-nvm-small.png', type: 'image/png', } tags: ['node', 'nvm', 'tutorial'] href: '/blog/como-settar-a-versao-default-do-node-usando-nvm' reactionsLength: 0 commentsLength: 0 --- For those who don't know yet, `nvm` is an excellent utility for when we want to switch Node versions quickly without having to uninstall the current one and install the desired one. For a long time I used the command: ```bash nvm use ``` To switch the Node version to a newer one, until a few days ago I discovered how to change the nvm default version in a very simple way. Just use the following command changing the `` part to the version you want to keep as default, for example `16.5.0`: ```bash nvm alias default ``` And that's it, now you'll always be using this nvm version at any time or in any terminal you open 😁. --- ## Books # Livros > Minha pequena biblioteca. ## Lido - Código Limpo - Robert C. Martin (★★★★★) ♥ - Arquitetura Limpa - Robert C. Martin - Domain-Driven Design - Eric Evans - The Pragmatic Programmer - Andrew Hunt, David Thomas (★★★★★) ♥ - Refatoração - Martin Fowler (★★★★☆) - Design Patterns - Gang of Four - Organize Sua Mente - Tiago Forte - O Lado Difícil das Situações Difíceis - Ben Horowitz - Data Science para Negócios - Foster Provost, Tom Fawcett - O Poder do Hábito - Charles Duhigg - A Coragem de Ser Imperfeito - Brené Brown - A Regra é Não Ter Regras - Reed Hastings, Erin Meyer - Lean Inception - Paulo Caroli - Product Backlog Building - Fábio Aguiar, Paulo Caroli - Gatilhos Mentais - Gustavo Ferreira - Sócrates Café - Christopher Phillips - Os Primeiros 90 Dias - Michael Watkins - Receita Previsível - Aaron Ross, Marylou Tyler - StoryBrand - Donald Miller - Comunicação Não Violenta - Marshall B. Rosenberg - Arrume a Sua Cama - William H. McRaven - Meditações - Marco Aurélio - As Leis da Simplicidade - John Maeda - Ganbatte - Nobuo Suzuki - Estruturas de Dados e Algoritmos com JavaScript - Loiane Groner - Entendendo Algoritmos - Aditya Bhargava - O Ato Criativo: Uma Forma de Ser - Rick Rubin - System Design Interview - Alex Xu - O Projeto Fênix - Gene Kim, Kevin Behr, George Spafford - Cracking the Coding Interview - Gayle Laakmann McDowell - 10 Cases do Design Brasileiro - Vol. 1 - Auresnede Pires Stephan - 10 Cases do Design Brasileiro - Vol. 2 - Auresnede Pires Stephan - 10 Cases do Design Brasileiro - Vol. 3 - Auresnede Pires Stephan - System Design Interview – An Insider's Guide: Volume 2 - Alex Xu, Sahn Lam --- # Books > My own little library. ## Read - Clean Code - Robert C. Martin (★★★★★) ♥ - Clean Architecture - Robert C. Martin - Domain-Driven Design - Eric Evans - The Pragmatic Programmer - Andrew Hunt, David Thomas (★★★★★) ♥ - Refactoring - Martin Fowler (★★★★☆) - Design Patterns - Gang of Four - Building a Second Brain - Tiago Forte - The Hard Thing About Hard Things - Ben Horowitz - Data Science for Business - Foster Provost, Tom Fawcett - The Power of Habit - Charles Duhigg - The Gifts of Imperfection - Brené Brown - No Rules Rules - Reed Hastings, Erin Meyer - Lean Inception - Paulo Caroli - Product Backlog Building - Fábio Aguiar, Paulo Caroli - Mental Triggers - Gustavo Ferreira - Socrates Café - Christopher Phillips - The First 90 Days - Michael Watkins - Predictable Revenue - Aaron Ross, Marylou Tyler - Building a StoryBrand - Donald Miller - Nonviolent Communication - Marshall B. Rosenberg - Make Your Bed - William H. McRaven - Meditations - Marco Aurélio - The Laws of Simplicity - John Maeda - Ganbatte - Nobuo Suzuki - Data Structures and Algorithms with JavaScript - Loiane Groner - Grokking Algorithms - Aditya Bhargava - The Creative Act: A Way of Being - Rick Rubin - System Design Interview - Alex Xu - The Phoenix Project - Gene Kim, Kevin Behr, George Spafford - Cracking the Coding Interview - Gayle Laakmann McDowell - 10 Cases of Brazilian Design - Vol. 1 - Auresnede Pires Stephan - 10 Cases of Brazilian Design - Vol. 2 - Auresnede Pires Stephan - 10 Cases of Brazilian Design - Vol. 3 - Auresnede Pires Stephan - System Design Interview – An Insider's Guide: Volume 2 - Alex Xu, Sahn Lam --- ## About # Sobre mim Rafael Thayto é um desenvolvedor com mais de 7 anos de experiência, apaixonado por resolver problemas e aprender novas tecnologias e boas práticas de desenvolvimento. Atuei em diversos projetos, sempre me dedicando ao máximo para garantir a entrega de soluções eficientes e de alta qualidade. Ele gosta bastante de jogar, conhecer pessoas e lugares novos. Posso ter certeza que se conversar com ele, em poucos minutos já terão criado um conexão. --- # About me Rafael Thayto is a developer with over 7 years of experience, passionate about solving problems and learning new technologies and good development practices. I have worked on several projects, always dedicating myself to the maximum to ensure the delivery of efficient and high quality solutions. He really enjoys playing, meeting new people and places. I can be sure that if you talk to him, in a few minutes you will have already created a connection. --- ## Hobbies # Hobbies > Coisas que eu gosto de fazer fora do código. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. --- # Hobbies > Things I like to do outside of code. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. --- ## Linktree # Rafael Thayto - Links > Senior Software Engineer - [lista de espera | linkedin](https://hub.la/r/lista-espera) - [Discord - Tutorandus Community](https://discord.gg/JXxC4xTdDU) - [Higlobe - USD 20$ pra você e pra mim](https://higlobe.com?grsf=rafael-xnn88r) - [❤️ Neovim Configs](https://github.com/rafa-thayto/lazy-thayto-vim) - [Newsletter - First Time Founder](https://thayto.substack.com/) - [Blog](https://thayto.com/blog) - [LinkedIn](https://www.linkedin.com/in/thayto/) - [Youtube](https://www.youtube.com/@thayto_dev) - [🎙️ DevSenior Cast](https://podcasters.spotify.com/pod/show/devseniorscast) - [GitHub](https://github.com/rafa-thayto) - [Instagram](https://www.instagram.com/thayto_dev/?utm_source=thayto.com) - [X/Twitter](https://x.com/thayto_dev) - [Bluesky](https://bsky.app/profile/thayto.dev) - [Twitch](https://twitch.tv/thayto_dev) - [Formação TS](https://hotm.art/hnWXd89A) - [CRUD com qualidade](https://bit.ly/3nLnPQZ) - [Estrutura de dados e algoritmos com JavaScript - Loiane](https://www.amazon.com.br/dp/8575226932?&_encoding=UTF8&tag=thayto-20&linkCode=ur2&linkId=a8887fab2c901ae25fb1855a72f0bc61&camp=1789&creative=9325) - [Dev.to](https://dev.to/thayto/) - [TabNews](https://www.tabnews.com.br/thayto) ---