Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Changesets

This project uses [Changesets](https://github.com/changesets/changesets) to manage semantic versioning and changelog generation.

## Creating a release entry

1. Run `bun run changeset` and follow the prompts to describe the change and choose the appropriate semver bump.
2. Commit the generated file in `.changeset/` along with your code changes.
3. When the changes land on the default branch, run `bun run version-packages` to update `package.json`, `bun.lock`, and the changelog.
4. Commit the version bump and changelog updates.
5. Push the commit and create a tag that matches the new version (for example, `v0.2.0`). Pushing the tag triggers the release workflow that builds and publishes the binaries.

> **Note**
> Update the `repo` field in `.changeset/config.json` to match the GitHub repository that hosts this project so that changelog entries link to the correct commits and pull requests.
16 changes: 16 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://unpkg.com/@changesets/config@2.3.1/schema.json",
"changelog": [
"@changesets/changelog-github",
{
"repo": "kevinHCH/charis"
}
],
"commit": false,
"fixed": [],
"linked": [],
"access": "restricted",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
}
34 changes: 34 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI

on:
push:
branches:
- main
- "release/**"
pull_request:

jobs:
build-and-test:
name: Build and test
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Install Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: "1.1.30"

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Type check
run: bun run typecheck

- name: Run tests
run: bun run test

- name: Build CLI binary
run: bun run build:bin
95 changes: 95 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Release

on:
push:
tags:
- 'v*'
workflow_dispatch:

jobs:
build:
name: Build binaries
strategy:
matrix:
include:
- os: ubuntu-latest
platform: linux
extension: ""
- os: macos-latest
platform: macos
extension: ""
- os: windows-latest
platform: windows
extension: ".exe"
runs-on: ${{ matrix.os }}

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Install Bun
uses: oven-sh/setup-bun@v1
with:
bun-version: "1.1.30"

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Build binary
run: bun run build:bin

- name: Package artifact
shell: bash
run: |
set -euo pipefail
mkdir -p dist
VERSION="${GITHUB_REF_NAME}"
ARCH="${RUNNER_ARCH,,}"
BINARY_NAME="charis${{ matrix.extension }}"
TARGET_NAME="charis-${VERSION}-${{ matrix.platform }}-${ARCH}${{ matrix.extension }}"
mv "$BINARY_NAME" "dist/$TARGET_NAME"

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: charis-${{ matrix.platform }}-${{ runner.arch }}
path: dist/*
retention-days: 7

release:
name: Publish release
runs-on: ubuntu-latest
needs: build

steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
path: dist
pattern: charis-*
merge-multiple: true

- name: Verify tag matches package version
run: |
VERSION_FROM_TAG="${GITHUB_REF_NAME#v}"
VERSION_FROM_PACKAGE=$(node -p "require('./package.json').version")
if [ "$VERSION_FROM_TAG" != "$VERSION_FROM_PACKAGE" ]; then
echo "Tag version ($VERSION_FROM_TAG) does not match package.json version ($VERSION_FROM_PACKAGE)." >&2
exit 1
fi

- name: Generate checksums
run: |
cd dist
sha256sum * > SHA256SUMS.txt

- name: Publish GitHub release
uses: softprops/action-gh-release@v2
with:
files: |
dist/*
generate_release_notes: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
11 changes: 6 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,16 @@ node_modules
out
dist
*.tgz
charis

# code coverage
coverage
*.lcov

# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
*.log
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# dotenv environment variable files
.env
Expand All @@ -30,7 +31,7 @@ report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# IntelliJ based IDEs
.idea

# Finder (MacOS) folder config
# Finder (macOS) folder config
.DS_Store
./generated-images
/generated-images

generated-images
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,21 @@ charis generate --help
charis gen help
```

## Available commands

All project scripts use Bun under the hood. Run them from the repository root unless noted otherwise.

| Command | Description |
|---------|-------------|
| `bun run dev` | Execute the CLI directly with Bun for local development. |
| `bun run build:bin` | Compile the TypeScript entry point into a standalone executable via `bun build --compile`. |
| `bun run typecheck` | Perform a TypeScript type-check without emitting build artifacts. |
| `bun run lint` | Alias of `bun run typecheck` for compatibility with linting workflows. |
| `bun run test` | Execute the Bun test suite. |
| `bun run changeset` | Create a new Changeset to record pending release notes. |
| `bun run version-packages` | Apply accumulated Changesets and bump the package version/changelog. |
| `bun run release:prepare` | Install dependencies with a frozen lockfile and build the distributable binary. |

### Troubleshooting key storage

- If your system does not provide a native keychain (for example missing `libsecret` on Linux), set
Expand All @@ -63,3 +78,66 @@ Every command has an alias. Some highlights:
- Default number of generated images: `1`.
- Default aspect ratio: `16:9`.
- Supported output formats: `png`, `jpg`, `webp` (default is `png`).

## Continuous integration and delivery

Automated checks run for every push and pull request through [GitHub Actions](.github/workflows/ci.yml).
The workflow installs dependencies with Bun, runs type-checking, executes the unit test suite, and compiles the
single-file binary via `bun build bin/charis.ts --compile` to ensure the CLI always builds successfully.

## Versioning strategy

Charis adopts [Changesets](https://github.com/changesets/changesets) for semantic versioning and release note generation.
To record a change, run `bun run changeset` and commit the generated markdown file. When the default branch is ready to
publish, execute `bun run version-packages` to bump the package version and changelog, commit the result, and push a tag
that follows the `v<major>.<minor>.<patch>` convention (for example `v0.2.0`). The release pipeline validates that the tag
matches the version declared in `package.json` to prevent accidental mismatches.

> Update the `repo` field in `.changeset/config.json` so that changelog links point to the correct GitHub repository.

## Release automation

Tagging a commit triggers the [release workflow](.github/workflows/release.yml), which:

1. Builds the single-file executable with Bun on Linux, macOS, and Windows runners.
2. Renames the binaries using the pattern `charis-vX.Y.Z-<platform>-<arch>` for easy distribution.
3. Publishes the artifacts alongside a SHA-256 checksum manifest in a GitHub Release with generated notes.

You can also run the workflow manually from the Actions tab via `workflow_dispatch`.

## Distributing updates

### Fresh installs

Download the latest binary for your operating system directly from the [GitHub Releases](https://github.com/REPO_OWNER/REPO_NAME/releases)
page and place it somewhere on your `PATH` (for example `~/bin`). Each release includes pre-built single-file executables
compiled with Bun's `--compile` flag so no runtime is required.

```bash
# Example for Linux/macOS (replace REPO_OWNER/REPO_NAME with the actual repository)
REPO="REPO_OWNER/REPO_NAME"
VERSION=$(curl -s https://api.github.com/repos/$REPO/releases/latest | jq -r .tag_name)
ASSET="charis-${VERSION}-linux-x64"
curl -L -o charis "https://github.com/$REPO/releases/download/${VERSION}/${ASSET}"
chmod +x charis
mv charis /usr/local/bin/
```

Windows users can download `charis-vX.Y.Z-windows-x64.exe` and copy it to a directory included in `%PATH%`.

### Updating existing installations

To roll out a new feature, publish a release as described above. Existing users simply download the new binary and
replace the previous executable. The command below automates the update for Unix-like systems:

```bash
REPO="REPO_OWNER/REPO_NAME"
LATEST=$(curl -s https://api.github.com/repos/$REPO/releases/latest | jq -r .tag_name)
ASSET="charis-${LATEST}-$(uname | tr '[:upper:]' '[:lower:]')-x64"
curl -L "https://github.com/$REPO/releases/download/${LATEST}/${ASSET}" -o ~/.local/bin/charis.tmp
chmod +x ~/.local/bin/charis.tmp
mv ~/.local/bin/charis.tmp ~/.local/bin/charis
```

Users can verify integrity by comparing the downloaded binary with the `SHA256SUMS.txt` file published in each release.

Loading
Loading