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
124 changes: 89 additions & 35 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ on:
- master

jobs:
# ------------------------------------------------------------------
# Build & test — runs on every push and PR
# ------------------------------------------------------------------
build:
runs-on: ubuntu-latest

permissions:
contents: write

steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -39,30 +39,98 @@ jobs:
- name: Run tests
run: npm test

# Release step - only runs on push to main/master (not PRs)
- name: Get version from manifest
id: version
# ------------------------------------------------------------------
# Release — only on push to main/master, after build passes.
# Analyses conventional commits since the last tag, bumps the
# version, commits the change, and creates a GitHub release.
# ------------------------------------------------------------------
release:
if: github.event_name == 'push'
needs: build
runs-on: ubuntu-latest

permissions:
contents: write

steps:
- name: Checkout (full history)
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Skip if version-bump commit
id: skip
run: |
VERSION=$(node -p "require('./manifest.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Release version: $VERSION"
MSG=$(git log -1 --pretty=format:"%s")
if echo "$MSG" | grep -q "\[skip ci\]"; then
echo "skip=true" >> $GITHUB_OUTPUT
echo "Skipping release — version-bump commit detected."
else
echo "skip=false" >> $GITHUB_OUTPUT
fi

- name: Check if release exists
if: github.event_name == 'push'
id: check_release
- name: Determine version bump from conventional commits
if: steps.skip.outputs.skip == 'false'
id: bump
run: |
if gh release view "${{ steps.version.outputs.version }}" > /dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Release ${{ steps.version.outputs.version }} already exists, skipping..."
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
if [ -z "$LATEST_TAG" ]; then
RANGE="HEAD"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Release ${{ steps.version.outputs.version }} does not exist, will create..."
RANGE="${LATEST_TAG}..HEAD"
fi
env:
GH_TOKEN: ${{ github.token }}

- name: Create Release
if: github.event_name == 'push' && steps.check_release.outputs.exists == 'false'
COMMITS=$(git log $RANGE --pretty=format:"%s" --no-merges)
echo "Commits since ${LATEST_TAG:-beginning}:"
echo "$COMMITS"

BUMP="none"
if echo "$COMMITS" | grep -qiE "(BREAKING[ -]CHANGE|^[a-z]+(\(.+\))?!:)"; then
BUMP="major"
elif echo "$COMMITS" | grep -qE "^feat(\(.+\))?!?:"; then
BUMP="minor"
elif echo "$COMMITS" | grep -qE "^fix(\(.+\))?!?:"; then
BUMP="patch"
fi

echo "bump=$BUMP" >> $GITHUB_OUTPUT
echo "Determined bump type: $BUMP"

- name: Setup Node.js
if: steps.skip.outputs.skip == 'false' && steps.bump.outputs.bump != 'none'
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install dependencies
if: steps.skip.outputs.skip == 'false' && steps.bump.outputs.bump != 'none'
run: npm ci

- name: Bump version
if: steps.skip.outputs.skip == 'false' && steps.bump.outputs.bump != 'none'
id: version
run: |
node version-bump.mjs --bump=${{ steps.bump.outputs.bump }}
VERSION=$(node -p "JSON.parse(require('fs').readFileSync('manifest.json','utf8')).version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "New version: $VERSION"

- name: Rebuild with new version
if: steps.skip.outputs.skip == 'false' && steps.bump.outputs.bump != 'none'
run: npm run build

- name: Commit and push version bump
if: steps.skip.outputs.skip == 'false' && steps.bump.outputs.bump != 'none'
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add manifest.json package.json versions.json package-lock.json
git commit -m "chore: release ${{ steps.version.outputs.version }} [skip ci]"
git push

- name: Create release
if: steps.skip.outputs.skip == 'false' && steps.bump.outputs.bump != 'none'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.version }}
Expand All @@ -74,17 +142,3 @@ jobs:
generate_release_notes: true
draft: false
prerelease: false

- name: Create Pre-Release
if: github.event_name != 'push'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version.outputs.version }}-${{ github.run_number }}
name: ${{ steps.version.outputs.version }}-${{ github.run_number }}
files: |
main.js
manifest.json
styles.css
generate_release_notes: true
draft: false
prerelease: true
40 changes: 40 additions & 0 deletions .husky/commit-msg
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env sh

# Enforce conventional commit format:
# type(scope)?: subject
#
# Allowed types:
# feat, fix, chore, docs, style, refactor, perf, test, build, ci, revert
#
# Examples:
# feat: add multi-repo support
# fix(sync): handle empty tree response
# chore: release 0.5.0 [skip ci]
# feat!: redesign settings API (BREAKING CHANGE)

commit_msg=$(cat "$1")

# Allow merge commits and revert commits generated by git
if echo "$commit_msg" | grep -qE "^(Merge |Revert )"; then
exit 0
fi

pattern="^(feat|fix|chore|docs|style|refactor|perf|test|build|ci|revert)(\(.+\))?!?: .+"

if ! echo "$commit_msg" | grep -qE "$pattern"; then
echo ""
echo "ERROR: Commit message does not follow Conventional Commits format."
echo ""
echo " Expected: <type>(<optional scope>): <description>"
echo ""
echo " Allowed types: feat, fix, chore, docs, style, refactor, perf, test, build, ci, revert"
echo ""
echo " Examples:"
echo " feat: add multi-repo support"
echo " fix(sync): handle empty tree response"
echo " docs: update README with versioning info"
echo ""
echo " Your message: $commit_msg"
echo ""
exit 1
fi
111 changes: 111 additions & 0 deletions DEVELOPMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Development

## Getting started

```bash
# Clone and install
git clone https://github.com/rhoades-brown/obsidian-github.git
cd obsidian-github
npm install

# Build
npm run build

# Run tests
npm test

# Watch mode (rebuilds on file changes)
npm run dev

# Lint
npm run lint
```

## Project structure

```text
obsidian-github/
├── main.ts # Plugin entry point
├── src/
│ ├── services/ # Core services (GitHub API, Sync, Logger)
│ ├── views/ # UI components (DiffView, SyncView)
│ ├── ui/ # Settings tab, modals
│ └── utils/ # Utility functions (file, diff, encoding)
├── tests/ # Jest test suites
└── styles.css # Plugin styles
```

## Conventional commits

All commit messages **must** follow the [Conventional Commits](https://www.conventionalcommits.org/) format:

```text
<type>(<optional scope>): <description>
```

A [husky](https://typicode.github.io/husky/) commit-msg hook validates every commit locally. Allowed types:

| Type | Purpose |
| ------ | --------- |
| `feat` | A new feature (triggers **minor** version bump) |
| `fix` | A bug fix (triggers **patch** version bump) |
| `docs` | Documentation only |
| `style` | Code style (formatting, semicolons, etc.) |
| `refactor` | Refactoring (no feature or fix) |
| `perf` | Performance improvement |
| `test` | Adding or updating tests |
| `build` | Build system or dependencies |
| `ci` | CI/CD changes |
| `chore` | Maintenance tasks |
| `revert` | Reverting a previous commit |

Append `!` after the type (e.g. `feat!:`) or include `BREAKING CHANGE` in the commit body to trigger a **major** version bump.

### Examples

```text
feat: add multi-repo support
fix(sync): handle empty tree response
docs: update README with versioning info
chore: update dependencies
feat!: redesign settings API
```

## Versioning & releases

Version bumping is fully automated — you never need to edit `manifest.json`, `package.json`, or `versions.json` manually.

When a PR is merged to `main`, the CI workflow:

1. Runs lint, build, and tests
2. Analyses commit messages since the last release tag
3. Determines the SemVer bump type (`major` / `minor` / `patch`)
4. Bumps the version in `manifest.json`, `package.json`, and `versions.json` via `version-bump.mjs`
5. Commits the version bump with `[skip ci]` to avoid re-triggering CI
6. Creates a GitHub release with `main.js`, `manifest.json`, and `styles.css` attached

If no `feat:` or `fix:` commits are found since the last release, no version bump or release is created.

### Manual version bumping

If you ever need to bump the version manually (e.g. for a pre-release), you can use:

```bash
npm version patch # 0.4.1 → 0.4.2
npm version minor # 0.4.1 → 0.5.0
npm version major # 0.4.1 → 1.0.0
```

This triggers the `version` script in `package.json`, which runs `version-bump.mjs` to keep all three version files in sync.

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run tests (`npm test`) and lint (`npm run lint`)
5. Commit using conventional commits (`git commit -m 'feat: add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request
43 changes: 2 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,36 +147,9 @@ Add custom patterns in Settings → GitHub Octokit → Ignore Patterns:
- `*.tmp` - All .tmp files
- `.obsidian/**` - All Obsidian settings (if desired)

## Development
## Development & contributing

```bash
# Clone and install
git clone https://github.com/rhoades-brown/obsidian-github.git
cd obsidian-github
npm install

# Build
npm run build

# Run tests
npm test

# Watch mode (rebuilds on file changes)
npm run dev
```

### Project Structure

```text
obsidian-github/
├── main.ts # Plugin entry point
├── src/
│ ├── services/ # Core services (GitHub API, Sync, Logger)
│ ├── views/ # UI components (DiffView, SyncView)
│ └── utils/ # Utility functions (file, diff, encoding)
├── tests/ # Jest test suites
└── styles.css # Plugin styles
```
See [DEVELOPMENT.md](DEVELOPMENT.md) for build instructions, project structure, conventional commit guidelines, and the automated versioning workflow.

## Troubleshooting

Expand Down Expand Up @@ -210,18 +183,6 @@ obsidian-github/
3. Open the debug console (macOS → cmd+option+i; Windows → ctrl+shift+i) to see real-time logs
4. Or click "View Logs" in settings to see recent entries

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Make your changes
4. Run tests (`npm test`)
5. Commit your changes (`git commit -m 'Add amazing feature'`)
6. Push to the branch (`git push origin feature/amazing-feature`)
7. Open a Pull Request

## License

MIT
2 changes: 1 addition & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
"author": "M Rhoades-Brown",
"authorUrl": "https://github.com/rhoades-brown",
"isDesktopOnly": false
}
}
Loading
Loading