diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 1b3c20b..4d1cdaf 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -12,7 +12,7 @@ jobs: - name: bun uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 with: - bun-version: 1.3.8 + bun-version: 1.3.9 - name: install run: bun install - name: trunk diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..536b433 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,97 @@ +name: deploy +on: + push: + branches: [staging, production] + pull_request: + branches: [staging] +permissions: + contents: read + pull-requests: write + deployments: write +concurrency: + group: deploy-${{ github.ref }} + cancel-in-progress: true +jobs: + deploy-staging: + if: github.ref == 'refs/heads/staging' && github.event_name == 'push' + runs-on: ubuntu-latest + environment: staging + steps: + - name: checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - name: bun + uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 + with: + bun-version: 1.3.9 + - name: install + run: bun install + - name: migrate + run: bun run db:migrate:staging + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + - name: build + run: bun run build:staging + - name: upload version + run: bun run wrangler versions upload + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + + deploy-production: + if: github.ref == 'refs/heads/production' && github.event_name == 'push' + runs-on: ubuntu-latest + environment: production + steps: + - name: checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - name: bun + uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 + with: + bun-version: 1.3.9 + - name: install + run: bun install + - name: migrate + run: bun run db:migrate:prod + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + - name: build + run: bun run build + - name: deploy + run: bun run wrangler deploy + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + + preview: + if: github.event_name == 'pull_request' + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + - name: bun + uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 + with: + bun-version: 1.3.9 + - name: install + run: bun install + - name: build + run: bun run build:staging + - name: upload version + id: upload + run: | + OUTPUT=$(bun run wrangler versions upload 2>&1) + echo "$OUTPUT" + VERSION_ID=$(echo "$OUTPUT" | grep -oP 'Worker Version ID:\s+\K[a-f0-9-]+') + echo "version_id=$VERSION_ID" >> "$GITHUB_OUTPUT" + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + - name: comment preview url + if: steps.upload.outputs.version_id != '' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + VERSION_ID: ${{ steps.upload.outputs.version_id }} + run: | + gh pr comment "$PR_NUMBER" --body "Preview version uploaded to **affirm** worker (staging D1). + + **Version ID:** \`$VERSION_ID\` + + Preview URL available in the [Cloudflare dashboard](https://dash.cloudflare.com/) under Workers > affirm > Versions." diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml index 61fe7ca..dfd614d 100644 --- a/.trunk/trunk.yaml +++ b/.trunk/trunk.yaml @@ -25,13 +25,13 @@ lint: - trunk-toolbox@0.5.4 - oxipng@10.1.0 - actionlint@1.7.10 - - checkov@3.2.500 + - checkov@3.2.501 - eslint@10.0.0 - git-diff-check - markdownlint@0.47.0 - prettier@3.8.1 - taplo@0.10.0 - - trufflehog@3.93.1 + - trufflehog@3.93.3 - yamllint@1.38.0 ignore: - linters: diff --git a/README.md b/README.md index 5cf6207..21278c5 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,74 @@ # `affirm` -![CI status](https://github.com/tecapps/affirm/actions/workflows/ci.yaml/badge.svg) ![DevSkim status](https://github.com/tecapps/affirm/actions/workflows/devskim.yaml/badge.svg) +![CI status](https://github.com/tecapps/affirm/actions/workflows/ci.yaml/badge.svg) ![Deploy status](https://github.com/tecapps/affirm/actions/workflows/deploy.yaml/badge.svg) ![DevSkim status](https://github.com/tecapps/affirm/actions/workflows/devskim.yaml/badge.svg) -Your first port of call should be the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. +Your first port of call should be the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn +more. > [!IMPORTANT] > Opinions ahead! The following are recommendations. Feel free to ignore them if you have better ideas. -All Nuxt modules except [NuxtUI](https://ui.nuxt.com) are installed and enabled. Notably, this includes [Nuxt Content](https://content.nuxtjs.org/), which will make our lives easier for copywriting by letting them write Markdown instead of HTML/Vue. +All Nuxt modules except [NuxtUI](https://ui.nuxt.com) are installed and enabled. Notably, this +includes [Nuxt Content](https://content.nuxtjs.org/), which will make our lives easier for copywriting by letting them +write Markdown instead of HTML/Vue. -But what are we going to use if not [NuxtUI](https://ui.nuxt.com)? Simple. [Tailwind](https://tailwindcss.com/) with [DaisyUI](https://daisyui.com/). This gives us a lot of flexibility while still providing a component library to speed up development. +But what are we going to use if not [NuxtUI](https://ui.nuxt.com)? Simple. [Tailwind](https://tailwindcss.com/) +with [DaisyUI](https://daisyui.com/). This gives us a lot of flexibility while still providing a component library to +speed up development. -The benefit of [DaisyUI](https://daisyui.com/) is that they don't dick about by having a "pro" version. The open-source version of DaisyUI is it. +The benefit of [DaisyUI](https://daisyui.com/) is that they don't dick about by having a "pro" version. The open-source +version of DaisyUI is it. -I'm not a frontend developer but I'd encourage use of the [Catppuccin](https://github.com/catppuccin) palette. There are dedicated packages for [the palette](https://github.com/catppuccin/palette), [DaisyUI](https://github.com/catppuccin/daisyui), and [Tailwind](https://github.com/catppuccin/tailwindcss). +I'm not a frontend developer but I'd encourage use of the [Catppuccin](https://github.com/catppuccin) palette. There are +dedicated packages +for [the palette](https://github.com/catppuccin/palette), [DaisyUI](https://github.com/catppuccin/daisyui), +and [Tailwind](https://github.com/catppuccin/tailwindcss). -Recommended VS Code extensions are configured for this workspace. Check the extensions view's _Recommended_ section. Feel free to add any you find useful. +Recommended VS Code extensions are configured for this workspace. Check the extensions view's _Recommended_ section. +Feel free to add any you find useful. > [!IMPORTANT] > Opinions end here. It's objectivity from here on. Mostly. ## Branch protections -The `main` branch is the production deployment. It's protected; changes to it can only come from a pull request, and that means a separate branch. +The `main` branch is the production deployment. It's protected; changes to it can only come from a pull request, and +that means a separate branch. Do your work in a branch named `username/purpose`; eg `daveio/fix-header`. -When it's ready to merge, submit a pull request. Two approvals are required on each PR. I ([@daveio](https://github.com/daveio)) will try to review all PRs and you can function as the other approver if you like. If I'm unavailable to review a PR, ask another team member to review it for you. Anyone can. +When it's ready to merge, submit a pull request. Two approvals are required on each PR. +I ([@daveio](https://github.com/daveio)) will try to review all PRs and you can function as the other approver if you +like. If I'm unavailable to review a PR, ask another team member to review it for you. Anyone can. -The purpose of this isn't to be a pain in the arse, it's to minimise the possibility of broken code reaching production. Your pushes to branches generate a `workers.dev` URL, so you can validate things before submitting a PR and save everyone a bunch of time. +The purpose of this isn't to be a pain in the arse, it's to minimise the possibility of broken code reaching production. +Your pushes to branches generate a `workers.dev` URL, so you can validate things before submitting a PR and save +everyone a bunch of time. > [!TIP] -> Please **sign your commits**. It's a major security win and it's not enormous hassle. You don't need a GnuPG key any more; Git supports signing with SSH keys now, and you probably use one of those to push anyway. See [the documentation](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) for more information. +> Please **sign your commits**. It's a major security win and it's not enormous hassle. You don't need a GnuPG key any +> more; Git supports signing with SSH keys now, and you probably use one of those to push anyway. +> See [the documentation](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits) +> for more information. ## Setup -Install [`bun`](https://bun.sh) if you haven't already. I suggest using [`mise`](https://github.com/jdx/mise), which you can also use to manage Node versions and a bunch of other stuff too. +Install [`bun`](https://bun.sh) if you haven't already. I suggest using [`mise`](https://github.com/jdx/mise), which you +can also use to manage Node versions and a bunch of other stuff too. There is a `mise.toml` file included in this repo. It will install everything you need. -The only exception is `trunk` which is a _massive_ pain in the arse to manage using `mise`. It'll be installed as a dev dependency and can be invoked through `bun run trunk`, or read the [installation documentation](https://docs.trunk.io/code-quality/overview/initialize-trunk) to install it globally if you prefer. +The only exception is `trunk` which is a _massive_ pain in the arse to manage using `mise`. It'll be installed as a dev +dependency and can be invoked through `bun run trunk`, or read +the [installation documentation](https://docs.trunk.io/code-quality/overview/initialize-trunk) to install it globally if +you prefer. > [!NOTE] -> It will also install a few extras; the CLIs for the major coding agents, and `rust` in case we decide to use `wasm` in the future. Feel free to edit it if you need to, just be aware you'll be changing it for everyone else too. +> It will also install a few extras; the CLIs for the major coding agents, and `rust` in case we decide to use `wasm` in +> the future. Feel free to edit it if you need to, just be aware you'll be changing it for everyone else too. > -> There are also `.tool-versions` and `.node-version` files, but they're more for Workers Builds. `mise` should be treated as the source of truth. +> There are also `.tool-versions` and `.node-version` files, but they're more for Workers Builds. `mise` should be +> treated as the source of truth. If you are using `mise`, simply run: @@ -73,16 +97,27 @@ bun dev ## Development -This repository hosts a **Nuxt 4** web application deployed to **Cloudflare Workers**. It uses **Bun** as the package manager and runtime for development scripts. +This repository hosts a **Nuxt 4** web application deployed to **Cloudflare Workers**. It uses **Bun** as the package +manager and runtime for development scripts. ### ⚡️ Essential Commands Run these commands with `bun`. - **Install dependencies**: `bun install` (or `bun run postinstall` to setup Trunk) -- **Development Server**: `bun run dev` (starts Nuxt dev server) -- **Build**: `bun run build` (builds for Cloudflare) -- **Deploy**: `bun run deploy` (deploys to Cloudflare via Wrangler) +- **Development Server**: `bun run dev` (starts Nuxt dev server with local D1) +- **Build**: + - `bun run build` (builds for production — uses `--envName=production`) + - `bun run build:staging` (builds for staging — uses `--envName=staging`) +- **Deploy** (prefer CI — see [Deployment](#deployment)): + - `bun run deploy` (build + deploy to production) + - `bun run deploy:staging` (build staging + upload non-promoted version) +- **Database**: + - `bun run db:generate` (generate migrations after schema changes) + - `bun run db:migrate` (apply migrations to local D1) + - `bun run db:migrate:staging` (apply migrations to staging D1) + - `bun run db:migrate:prod` (apply migrations to production D1) + - `bun run db:studio:staging` / `bun run db:studio:prod` (Drizzle Studio) - **Lint & Format**: - `bun run lint:fix` (Run all linters and fix issues) - `bun run format` (Format code with Prettier and Trunk) @@ -113,7 +148,8 @@ This project follows the **Nuxt 4** directory structure (source in `app/`). #### Vue & TypeScript - Use **Composition API** with `