From b6641d4d4a3a17c140ef67e138cf6e3ae9783342 Mon Sep 17 00:00:00 2001 From: "J. Lewis" <6710419+lewxdev@users.noreply.github.com> Date: Sun, 2 Jun 2024 11:01:13 -0400 Subject: [PATCH] feat: initial setup reconfigure the repository as a bun project in order to make use of the single-file executable feature. this should significantly improve the dx and make the codebase easier to understand and maintain. - add configuration for typescript, vscode, husky, commitlint, lint-staged, and biome to better manage the development workflow - add community health files (CODEOWNERS, LICENSE, README, etc.) - add a github action to automate the process of releasing the cli executable - add cli.ts as the entry point for the cli - add install.zsh to easily install the released executable locally --- .github/CODEOWNERS | 3 ++ .github/workflows/release.yml | 25 +++++++++++++++ .gitignore | 52 +++++++++++++++++++++++++++++++ .husky/commit-msg | 3 ++ .vscode/extensions.json | 10 ++++++ .vscode/settings.json | 15 +++++++++ LICENSE.md | 20 ++++++++++++ README.md | 56 ++++++++++++++++++++++++++++++++++ biome.json | 28 +++++++++++++++++ bun.lockb | Bin 0 -> 67180 bytes cli.ts | 27 ++++++++++++++++ install.zsh | 30 ++++++++++++++++++ package.json | 42 +++++++++++++++++++++++++ tsconfig.json | 9 ++++++ 14 files changed, 320 insertions(+) create mode 100644 .github/CODEOWNERS create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100755 .husky/commit-msg create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 biome.json create mode 100755 bun.lockb create mode 100644 cli.ts create mode 100644 install.zsh create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..93c1e31 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# learn more: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners + +* @lewxdev diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5bf4285 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +name: 🚀 Create new release on GitHub +on: + push: + +permissions: + contents: write + +jobs: + publish: + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 + + - run: | + bun install + bun run build + echo "dotfiles=dist/dotfiles" >> $GITHUB_ENV + + - env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh release create v$($dotfiles --version) $dotfiles --generate-notes \ + --prerelease=${{ github.ref != 'refs/heads/main' }} \ + --target=${{ github.ref }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..845ab9f --- /dev/null +++ b/.gitignore @@ -0,0 +1,52 @@ +# based on the githhub/node.gitignore template +# see: https://github.com/github/gitignore/blob/main/Node.gitignore + +# builds +dist +out +*.tgz + +# dependencies +node_modules + +# environment +.env +.env.*.local +.env.local + +# logs +logs +*.log +*-debug.log* +*-error.log* + +# miscellaneous +.cache +.temp +.DS_Store + +# runtime +pids +*.pid +*.seed +*.pid.lock + +# eslint cache +.eslintcache + +# npm cache +.npm + +# repl history +.node_repl_history + +# typescript cache +*.tsbuildinfo + +# addons +# see: https://nodejs.org/api/addons.html +build/Release + +# reports +# see: https://nodejs.org/api/report.html +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000..1a71a3c --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,3 @@ +#!/usr/bin/env zsh + +bun run commitlint --edit $1 diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..d1164c3 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + "recommendations": [ + "biomejs.biome", + "bmalehorn.shell-syntax", + "github.vscode-github-actions", + "joshbolduc.commitlint", + "streetsidesoftware.code-spell-checker", + "yoavbls.pretty-ts-errors" + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5cb3338 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,15 @@ +{ + "cSpell.words": ["dotfiles"], + + "editor.codeActionsOnSave": { + "quickfix.biome": "explicit", + "source.organizeImports.biome": "explicit" + }, + "editor.defaultFormatter": "biomejs.biome", + "editor.formatOnSave": true, + + "files.associations": { + "gitignore": "gitignore", + "gitmessage": "git-commit" + } +} diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..cd37869 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,20 @@ +# The MIT License (MIT) + +Copyright © 2021-Present, [lewxdev](https://github.com/lewxdev) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the “Software”), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ddff7e1 --- /dev/null +++ b/README.md @@ -0,0 +1,56 @@ +``` + __ __ ____ __ + ___/ /__ / /_/ _(_) /__ ___ +/ _ / _ \/ __/ _/ / / -_|_-< +\_,_/\___/\__/_//_/_/\__/___/ +``` + +# @lewxdev/dotfiles + +This repository consists of infrequently updated personal configurations, +dotfiles, and scripts for a macOS-based environment. It provides the following: + +- a CLI as a [single-file executable](https://bun.sh/docs/bundler/executables) + that walks through a configurable installation process +- a one-time installation script that adds the `dotfiles` CLI to the PATH + +Although I cannot guarantee it will work for you, feel free to build on and +learn from this setup. If you have any questions or suggestions, please open an +issue. + +## Usage + +When installing for the first time, run the following command: + +```shell +curl -fsSL https://raw.githubusercontent.com/lewxdev/dotfiles/main/install.zsh | zsh +``` + +After installation, you can run the `dotfiles` CLI to manage your dotfiles: + +```shell +dotfiles --help +``` + +## Development + +```shell +# install dependencies +bun install +``` + +```shell +# invoke the CLI +bun start +``` + +```shell +# build the executable +bun run build +``` + +```shell +# update CLI installation +# note: this may prompt you for your password +bun run update +``` diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..29e456e --- /dev/null +++ b/biome.json @@ -0,0 +1,28 @@ +{ + "$schema": "node_modules/@biomejs/biome/configuration_schema.json", + "organizeImports": { + "enabled": true + }, + "files": { + "ignoreUnknown": true + }, + "formatter": { + "indentStyle": "tab" + }, + "linter": { + "rules": { + "recommended": true + } + }, + "json": { + "parser": { + "allowComments": true + } + }, + "vcs": { + "enabled": true, + "clientKind": "git", + "defaultBranch": "main", + "useIgnoreFile": true + } +} diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..e56384e557488c6de4f9601bd15e04649a30523b GIT binary patch literal 67180 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 literal 0 HcmV?d00001 diff --git a/cli.ts b/cli.ts new file mode 100644 index 0000000..4278396 --- /dev/null +++ b/cli.ts @@ -0,0 +1,27 @@ +import { $ } from "bun"; +import { program } from "commander"; + +import packageJson from "@/package.json"; + +const LOCAL_PATH = "~/projects/dotfiles"; +const REMOTE_PATH = packageJson.repository.url; + +program + .name("dotfiles") + .description(`a CLI to manage ${packageJson.description}`) + .version(packageJson.version); + +program + .command("install") + .description("run the configurable installation script") + .action(async () => { + try { + // check if local repository exists + await $`test -d ${LOCAL_PATH}`; + } catch { + // create if local repository is missing + await $`git clone ${REMOTE_PATH} ${LOCAL_PATH}`; + } + }); + +await program.parseAsync(); diff --git a/install.zsh b/install.zsh new file mode 100644 index 0000000..b8ccb57 --- /dev/null +++ b/install.zsh @@ -0,0 +1,30 @@ +#!/usr/bin/env zsh + +# get the latest release from GitHub +# TODO: update version to latest release before merging +echo "downloading the dotfiles CLI..." +curl -fsSL -o /usr/local/bin/dotfiles https://github.com/lewxdev/dotfiles/releases/download/v2.0.0/dotfiles + +# make the file executable +echo "completing installation..." +chmod +x /usr/local/bin/dotfiles + +if ! command -v dotfiles &> /dev/null; then + echo "error: dotfiles is not installed, try again or install manually" + exit 1 +fi + +# post installation message +echo " + __ __ ____ __ + ___/ /__ / /_/ _(_) /__ ___ +/ _ / _ \/ __/ _/ / / -_|_-< +\_,_/\___/\__/_//_/_/\__/___/ v$(dotfiles --version) + +successfully installed the dotfiles CLI 🎉 + +next steps: + 1. run 'dotfiles install' + 2. edit '~/projects/dotfiles' + +see 'dotfiles --help' for more" diff --git a/package.json b/package.json new file mode 100644 index 0000000..5f7c4f6 --- /dev/null +++ b/package.json @@ -0,0 +1,42 @@ +{ + "name": "@lewxdev/dotfiles", + "version": "2.0.0", + "description": "personal configurations, dotfiles, and scripts for a macOS-based environment", + "license": "MIT", + "author": "J. Lewis (https://lewx.dev)", + "module": "cli.ts", + "type": "module", + "repository": { + "type": "git", + "url": "https://github.com/lewxdev/dotfiles.git" + }, + "scripts": { + "start": "bun run cli.ts", + "build": "bun build cli.ts --compile --minify --outfile dist/dotfiles", + "update": "bun run build && sudo mv dist/dotfiles /usr/local/bin", + "prepare": "husky", + "format": "biome format --write .", + "lint": "biome lint ." + }, + "dependencies": { + "commander": "^12.1.0" + }, + "devDependencies": { + "@biomejs/biome": "1.7.3", + "@commitlint/cli": "^19.3.0", + "@commitlint/config-conventional": "^19.2.2", + "@tsconfig/bun": "latest", + "@tsconfig/strictest": "^2.0.5", + "@types/bun": "latest", + "husky": "^9.0.11", + "lint-staged": "^15.2.5", + "typescript": "^5.4.5" + }, + "commitlint": { + "extends": ["@commitlint/config-conventional"] + }, + "lint-staged": { + "*": "biome format --write", + "*.ts": "biome lint --apply" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..0832493 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": ["@tsconfig/bun", "@tsconfig/strictest"], + "compilerOptions": { + "baseUrl": ".", + "paths": { + "@/*": ["./*"] + } + } +}