Advanced Scripting
Advanced patterns, cron automation, testing, and best practices for Heyweek CLI scripts.
Advanced patterns, cron automation, testing, and best practices for Heyweek CLI scripts.
Advanced Patterns
State management
Maintain state between runs by saving a value to a file. This example records the project you last logged against:
<span class="token shebang important">#!/bin/bash</span>
<span class="token comment"># stateful-tracker.sh</span>
<span class="token assign-left variable">STATE_FILE</span><span class="token operator">=</span><span class="token string">"<span class="token environment constant">$HOME</span>/.hw-script-state"</span>
<span class="token comment"># Load previous state</span>
<span class="token punctuation">[</span> <span class="token parameter variable">-f</span> <span class="token string">"<span class="token variable">$STATE_FILE</span>"</span> <span class="token punctuation">]</span> <span class="token operator">&&</span> <span class="token builtin class-name">source</span> <span class="token string">"<span class="token variable">$STATE_FILE</span>"</span>
<span class="token comment"># Pick the first project as the current one</span>
<span class="token assign-left variable">CURRENT</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span>hw project list <span class="token parameter variable">-q</span> <span class="token string">'.[0].id'</span><span class="token variable">)</span></span>
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$CURRENT</span>"</span> <span class="token operator">!=</span> <span class="token string">"<span class="token variable">${LAST_PROJECT<span class="token operator">:-</span>}</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
<span class="token builtin class-name">echo</span> <span class="token string">"Active project changed from <span class="token variable">${LAST_PROJECT<span class="token operator">:-</span>none}</span> to <span class="token variable">$CURRENT</span>"</span>
<span class="token builtin class-name">echo</span> <span class="token string">"LAST_PROJECT='<span class="token variable">$CURRENT</span>'"</span> <span class="token operator">></span> <span class="token string">"<span class="token variable">$STATE_FILE</span>"</span>
<span class="token keyword">fi</span>
Git hook integration
Log time automatically from a git post-commit hook by mapping the branch to a project:
<span class="token shebang important">#!/bin/bash</span>
<span class="token comment"># .git/hooks/post-commit</span>
<span class="token assign-left variable">COMMIT_MSG</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token function">git</span> log <span class="token parameter variable">-1</span> <span class="token parameter variable">--pretty</span><span class="token operator">=</span>%B<span class="token variable">)</span></span>
<span class="token assign-left variable">BRANCH</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$(</span><span class="token function">git</span> branch --show-current<span class="token variable">)</span></span>
<span class="token comment"># Map branch to a project ID</span>
<span class="token keyword">case</span> <span class="token string">"<span class="token variable">$BRANCH</span>"</span> <span class="token keyword">in</span>
feature/*<span class="token punctuation">)</span> <span class="token assign-left variable">PROJECT</span><span class="token operator">=</span><span class="token string">"proj-123"</span> <span class="token punctuation">;</span><span class="token punctuation">;</span>
bugfix/*<span class="token punctuation">)</span> <span class="token assign-left variable">PROJECT</span><span class="token operator">=</span><span class="token string">"proj-456"</span> <span class="token punctuation">;</span><span class="token punctuation">;</span>
*<span class="token punctuation">)</span> <span class="token assign-left variable">PROJECT</span><span class="token operator">=</span><span class="token string">"proj-internal"</span> <span class="token punctuation">;</span><span class="token punctuation">;</span>
<span class="token keyword">esac</span>
hw log create <span class="token punctuation">\</span>
<span class="token parameter variable">-d</span> <span class="token string">"Development: <span class="token variable">$COMMIT_MSG</span>"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">-p</span> <span class="token string">"<span class="token variable">$PROJECT</span>"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">-s</span> <span class="token string">"09:00"</span> <span class="token punctuation">\</span>
<span class="token parameter variable">-e</span> <span class="token string">"09:15"</span>
Cron Automation
Schedule real hw commands with cron. Edit your crontab with crontab -e:
<span class="token comment"># Start a timer at 9 AM on weekdays</span>
<span class="token number">0</span> <span class="token number">9</span> * * <span class="token number">1</span>-5 /usr/local/bin/hw timer start <span class="token parameter variable">-d</span> <span class="token string">"Starting work day"</span>
<span class="token comment"># Stop the timer at 5 PM on weekdays</span>
<span class="token number">0</span> <span class="token number">17</span> * * <span class="token number">1</span>-5 /usr/local/bin/hw timer stop
<span class="token comment"># Run a daily logging script at 6 PM</span>
<span class="token number">0</span> <span class="token number">18</span> * * <span class="token number">1</span>-5 /home/user/scripts/daily-log.sh
<span class="token comment"># Renew the auth token every morning so scheduled jobs stay authenticated</span>
<span class="token number">0</span> <span class="token number">8</span> * * * /usr/local/bin/hw auth renew
Testing Scripts
Test your automation with a small assertion helper. This exercises the real timer commands end to end:
<span class="token shebang important">#!/bin/bash</span>
<span class="token comment"># test-timer-script.sh</span>
<span class="token function-name function">assert_ok</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$1</span>"</span> <span class="token parameter variable">-eq</span> <span class="token number">0</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
<span class="token builtin class-name">echo</span> <span class="token string">"ok: <span class="token variable">$2</span>"</span>
<span class="token keyword">else</span>
<span class="token builtin class-name">echo</span> <span class="token string">"FAIL: <span class="token variable">$2</span>"</span>
<span class="token builtin class-name">exit</span> <span class="token number">1</span>
<span class="token keyword">fi</span>
<span class="token punctuation">}</span>
<span class="token comment"># Start a timer</span>
hw timer start <span class="token parameter variable">-d</span> <span class="token string">"Test timer"</span>
assert_ok <span class="token variable">$?</span> <span class="token string">"timer should start"</span>
<span class="token comment"># Status should succeed while a timer is running</span>
hw timer status <span class="token operator">></span>/dev/null <span class="token operator"><span class="token file-descriptor important">2</span>></span><span class="token file-descriptor important">&1</span>
assert_ok <span class="token variable">$?</span> <span class="token string">"timer should be running"</span>
<span class="token comment"># Stop the timer</span>
hw timer stop
assert_ok <span class="token variable">$?</span> <span class="token string">"timer should stop"</span>
<span class="token builtin class-name">echo</span> <span class="token string">"All tests passed!"</span>
Best Practices
Retry on failure
Wrap commands in a retry loop to survive transient errors:
<span class="token shebang important">#!/bin/bash</span>
<span class="token comment"># robust-script.sh</span>
<span class="token function-name function">retry</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token builtin class-name">local</span> <span class="token assign-left variable">n</span><span class="token operator">=</span><span class="token number">1</span> <span class="token assign-left variable">max</span><span class="token operator">=</span><span class="token number">5</span> <span class="token assign-left variable">delay</span><span class="token operator">=</span><span class="token number">2</span>
<span class="token keyword">until</span> <span class="token string">"<span class="token variable">$@</span>"</span><span class="token punctuation">;</span> <span class="token keyword">do</span>
<span class="token keyword">if</span> <span class="token punctuation">[</span> <span class="token string">"<span class="token variable">$n</span>"</span> <span class="token parameter variable">-lt</span> <span class="token string">"<span class="token variable">$max</span>"</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
<span class="token assign-left variable">n</span><span class="token operator">=</span><span class="token variable"><span class="token variable">$((</span>n <span class="token operator">+</span> <span class="token number">1</span><span class="token variable">))</span></span>
<span class="token builtin class-name">echo</span> <span class="token string">"Command failed. Attempt <span class="token variable">$n</span>/<span class="token variable">$max</span>..."</span>
<span class="token function">sleep</span> <span class="token string">"<span class="token variable">$delay</span>"</span>
<span class="token keyword">else</span>
<span class="token builtin class-name">echo</span> <span class="token string">"Command failed after <span class="token variable">$n</span> attempts."</span>
<span class="token builtin class-name">return</span> <span class="token number">1</span>
<span class="token keyword">fi</span>
<span class="token keyword">done</span>
<span class="token punctuation">}</span>
retry hw timer start <span class="token parameter variable">-d</span> <span class="token string">"Important task"</span>
Logging
Record what your automation does so you can audit it later:
<span class="token shebang important">#!/bin/bash</span>
<span class="token comment"># logged-automation.sh</span>
<span class="token assign-left variable">LOG_FILE</span><span class="token operator">=</span><span class="token string">"<span class="token environment constant">$HOME</span>/.hw-automation.log"</span>
<span class="token function-name function">log</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token builtin class-name">echo</span> <span class="token string">"[<span class="token variable"><span class="token variable">$(</span><span class="token function">date</span> <span class="token string">'+%Y-%m-%d %H:%M:%S'</span><span class="token variable">)</span></span>] <span class="token variable">$1</span>"</span> <span class="token operator">>></span> <span class="token string">"<span class="token variable">$LOG_FILE</span>"</span>
<span class="token punctuation">}</span>
log <span class="token string">"Script started"</span>
<span class="token keyword">if</span> hw timer start <span class="token parameter variable">-d</span> <span class="token string">"Automated task"</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
log <span class="token string">"Timer started successfully"</span>
<span class="token keyword">else</span>
log <span class="token string">"ERROR: Failed to start timer"</span>
<span class="token builtin class-name">exit</span> <span class="token number">1</span>
<span class="token keyword">fi</span>
log <span class="token string">"Script completed"</span>
Check authentication first
Long-running and scheduled scripts should confirm they are authenticated before doing real work:
<span class="token shebang important">#!/bin/bash</span>
<span class="token keyword">if</span> <span class="token operator">!</span> hw user ctx <span class="token operator">></span>/dev/null <span class="token operator"><span class="token file-descriptor important">2</span>></span><span class="token file-descriptor important">&1</span><span class="token punctuation">;</span> <span class="token keyword">then</span>
<span class="token builtin class-name">echo</span> <span class="token string">"Not authenticated. Run 'hw auth login' or 'hw auth renew'."</span>
<span class="token builtin class-name">exit</span> <span class="token number">1</span>
<span class="token keyword">fi</span>
hw timer start <span class="token parameter variable">-d</span> <span class="token string">"Background task"</span>
ā Back to CLI scripting.