Skip to content

Commit 899b5b8

Browse files
authored
Merge pull request #1 from Qbandev/feat/v1.1-polish
feat(install): add Wave detection, JSON validation, and release automation
2 parents 8aefd11 + eb14219 commit 899b5b8

9 files changed

Lines changed: 573 additions & 201 deletions

File tree

.agents.md

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ wave-notes-setup/
2323
│ └── run_tests.sh # Local test runner
2424
└── .github/
2525
└── workflows/
26-
└── test.yml # CI workflow (shellcheck + bats)
26+
├── test.yml # CI workflow (shellcheck + bats)
27+
└── release-please.yml # Automated releases with release-please
2728
```
2829

2930
## Key Components
@@ -33,8 +34,10 @@ wave-notes-setup/
3334
- **Configuration Precedence**: defaults → ~/.wave-notes.conf → env vars
3435
- **Key Functions**:
3536
- `detect_waveterm_config()` - Finds Wave Terminal config path (supports v0.9+ and older)
37+
- `check_wave_installed()` - Blocks installation if Wave Terminal not found
38+
- `validate_widgets()` - Validates JSON with jq or python3 fallback
3639
- `install_scratchpad_script()` - Generates ~/bin/wave-scratch.sh
37-
- `install_widgets()` - Merges widgets into widgets.json (jq or Python fallback)
40+
- `install_widgets()` - Merges widgets into widgets.json with validation and rollback
3841
- `get_max_display_order()` - Preserves existing widget ordering
3942

4043
### 2. wave-scratch.sh (Generated)
@@ -124,7 +127,7 @@ This is a Wave Terminal limitation. Minimize with:
124127
## Development Workflow
125128

126129
```bash
127-
# Run automated tests (46 tests)
130+
# Run automated tests (54 tests)
128131
./test/run_tests.sh
129132

130133
# Or run bats directly
@@ -145,7 +148,7 @@ cat ~/.config/waveterm/widgets.json | jq .
145148

146149
## Test Coverage
147150

148-
The test suite includes **46 tests** covering:
151+
The test suite includes **54 tests** covering:
149152

150153
### Unit Tests (install.bats)
151154
- `detect_waveterm_config()` - Path detection for both v0.9+ and older
@@ -154,7 +157,9 @@ The test suite includes **46 tests** covering:
154157
- `get_existing_order()` - Preserve existing widget positions
155158
- `create_directories()` - Directory creation
156159
- `install_scratchpad_script()` - Script generation and content verification
157-
- `install_widgets()` - JSON merging, backup creation, no hardcoded colors
160+
- `install_widgets()` - JSON merging, backup creation, validation, rollback
161+
- `check_wave_installed()` - Wave Terminal detection (config dirs, wsh command)
162+
- `validate_widgets()` - JSON validation with jq/python3 fallback
158163

159164
### Unit Tests (uninstall.bats)
160165
- `remove_widgets()` - Selective removal of notes-* widgets
@@ -175,13 +180,13 @@ The test suite includes **46 tests** covering:
175180
| `detect_waveterm_config()` | Find Wave Terminal config directory | None |
176181
| `load_config()` | Load configuration (defaults → file → env) | None |
177182
| `check_macos()` | Verify running on macOS | None |
178-
| `check_wave_terminal()` | Verify Wave Terminal is installed | None |
179-
| `check_wsh()` | Check if wsh command is available | None |
183+
| `check_wave_installed()` | Block install if Wave Terminal not found | None |
184+
| `validate_widgets()` | Validate JSON with jq or python3 fallback | `$1`: widgets.json path |
180185
| `create_directories()` | Create notes and bin directories | None |
181186
| `install_scratchpad_script()` | Generate wave-scratch.sh | None |
182187
| `get_max_display_order()` | Find highest widget order | `$1`: widgets.json path |
183188
| `get_existing_order()` | Get widget's current order | `$1`: path, `$2`: key, `$3`: default |
184-
| `install_widgets()` | Merge widgets into widgets.json | None |
189+
| `install_widgets()` | Merge widgets with validation and rollback | None |
185190
| `run_uninstall()` | Execute uninstall procedure | None |
186191
| `parse_args()` | Parse CLI arguments | `$@`: arguments |
187192

@@ -224,8 +229,10 @@ The scripts use `set -euo pipefail`:
224229

225230
### Error Recovery
226231

227-
- **Backup before modify**: widgets.json is backed up before changes
232+
- **Backup before modify**: widgets.json is backed up with chmod 600 before changes
228233
- **Atomic writes**: Use temp file + mv for JSON writes
234+
- **JSON validation**: Validate widgets.json after generation (jq or python3 fallback)
235+
- **Rollback on failure**: Restore backup if validation fails; remove invalid file on fresh install
229236
- **Origin verification**: Only delete scripts with "Generated by" header
230237
- **User confirmation**: Destructive operations require explicit consent
231238

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
name: Release Please
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
8+
permissions:
9+
contents: write
10+
pull-requests: write
11+
12+
jobs:
13+
release-please:
14+
runs-on: ubuntu-latest
15+
outputs:
16+
release_created: ${{ steps.release.outputs.release_created }}
17+
tag_name: ${{ steps.release.outputs.tag_name }}
18+
version: ${{ steps.release.outputs.version }}
19+
20+
steps:
21+
- uses: googleapis/release-please-action@v4
22+
id: release
23+
with:
24+
release-type: simple
25+
package-name: wave-notes-setup
26+
27+
# Run tests and create GitHub release with install instructions
28+
publish-release:
29+
needs: release-please
30+
if: ${{ needs.release-please.outputs.release_created }}
31+
runs-on: macos-latest
32+
33+
steps:
34+
- uses: actions/checkout@v4
35+
36+
- name: Install test tools
37+
run: brew install bats-core shellcheck
38+
39+
- name: Run ShellCheck
40+
run: shellcheck install.sh uninstall.sh
41+
42+
- name: Run tests
43+
run: bats test/*.bats
44+
45+
- name: Calculate SHA256
46+
id: sha256
47+
run: |
48+
VERSION="${{ needs.release-please.outputs.tag_name }}"
49+
TARBALL_URL="https://github.com/${{ github.repository }}/archive/${VERSION}.tar.gz"
50+
curl -sL "$TARBALL_URL" -o release.tar.gz
51+
SHA256=$(shasum -a 256 release.tar.gz | cut -d' ' -f1)
52+
echo "sha256=$SHA256" >> $GITHUB_OUTPUT
53+
54+
- name: Update release notes
55+
uses: actions/github-script@v7
56+
env:
57+
VERSION: ${{ needs.release-please.outputs.tag_name }}
58+
SHA256: ${{ steps.sha256.outputs.sha256 }}
59+
with:
60+
script: |
61+
const { VERSION, SHA256 } = process.env;
62+
63+
// Get the existing release
64+
const { data: release } = await github.rest.repos.getReleaseByTag({
65+
owner: context.repo.owner,
66+
repo: context.repo.repo,
67+
tag: VERSION
68+
});
69+
70+
// Append installation instructions
71+
const installInstructions = `
72+
73+
## Installation
74+
75+
### Homebrew (Recommended)
76+
\`\`\`bash
77+
brew tap qbandev/wave-notes-setup
78+
brew install wave-notes-setup
79+
wave-notes-setup
80+
\`\`\`
81+
82+
### curl
83+
\`\`\`bash
84+
curl -fsSL https://raw.githubusercontent.com/qbandev/wave-notes-setup/${VERSION}/install.sh | bash
85+
\`\`\`
86+
87+
### Manual
88+
\`\`\`bash
89+
git clone https://github.com/qbandev/wave-notes-setup.git
90+
cd wave-notes-setup
91+
git checkout ${VERSION}
92+
./install.sh
93+
\`\`\`
94+
95+
## Homebrew Formula Update
96+
97+
Update \`Formula/wave-notes-setup.rb\`:
98+
\`\`\`ruby
99+
url "https://github.com/qbandev/wave-notes-setup/archive/${VERSION}.tar.gz"
100+
sha256 "${SHA256}"
101+
\`\`\`
102+
`;
103+
104+
await github.rest.repos.updateRelease({
105+
owner: context.repo.owner,
106+
repo: context.repo.repo,
107+
release_id: release.id,
108+
body: release.body + installInstructions
109+
});
110+
111+
console.log(`Release ${VERSION} updated with install instructions`);
112+
console.log(`SHA256 for Homebrew: ${SHA256}`);
113+
114+
# Notify about formula update needed
115+
notify-homebrew:
116+
needs: [release-please, publish-release]
117+
if: ${{ needs.release-please.outputs.release_created }}
118+
runs-on: ubuntu-latest
119+
120+
steps:
121+
- name: Create issue for Homebrew update
122+
uses: actions/github-script@v7
123+
env:
124+
VERSION: ${{ needs.release-please.outputs.tag_name }}
125+
with:
126+
script: |
127+
const version = process.env.VERSION;
128+
129+
// Check if issue already exists
130+
const { data: issues } = await github.rest.issues.listForRepo({
131+
owner: context.repo.owner,
132+
repo: context.repo.repo,
133+
state: 'open',
134+
labels: 'homebrew,release'
135+
});
136+
137+
const existingIssue = issues.find(i => i.title.includes(version));
138+
if (existingIssue) {
139+
console.log('Issue already exists for this version');
140+
return;
141+
}
142+
143+
await github.rest.issues.create({
144+
owner: context.repo.owner,
145+
repo: context.repo.repo,
146+
title: `Update Homebrew formula for ${version}`,
147+
body: `A new release ${version} has been published.
148+
149+
## Action Required
150+
151+
Update the Homebrew formula with the new SHA256 from the release notes.
152+
153+
1. Go to [Releases](https://github.com/${context.repo.owner}/${context.repo.repo}/releases/tag/${version})
154+
2. Copy the SHA256 value
155+
3. Update \`Formula/wave-notes-setup.rb\`:
156+
- Update the \`url\` to point to ${version}
157+
- Update the \`sha256\` value
158+
4. Commit and push the changes
159+
5. Close this issue
160+
161+
This issue was automatically created by the release workflow.`,
162+
labels: ['homebrew', 'release']
163+
});

0 commit comments

Comments
 (0)