Skip to content

Monthly Time Reporting #12

Monthly Time Reporting

Monthly Time Reporting #12

name: Monthly Time Reporting
description: Downloads and converts a time report from Toggl to Harvest format.
on:
workflow_dispatch:
inputs:
start-date:
description: "Start date for the report period (YYYY-MM-DD)"
required: false
end-date:
description: "End date for the report period (YYYY-MM-DD)"
required: false
jobs:
time-reporting:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version-file: .github/workflows/.python-version
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -e .
- name: Prep configuration
id: config
env:
CFGDIR: "${{ github.workspace }}/.config"
GAMCFGDIR: "${{ github.workspace }}/.config/gam"
run: |
mkdir -p $GAMCFGDIR/gamcache
mkdir -p $GAMCFGDIR/Downloads
cat > $CFGDIR/toggl-project-info.json <<- EOM
${{ secrets.TOGGL_PROJECT_INFO }}
EOM
echo "TOGGL_PROJECT_INFO=$CFGDIR/toggl-project-info.json" >> $GITHUB_OUTPUT
cat > $CFGDIR/toggl-user-info.json <<- EOM
${{ secrets.TOGGL_USER_INFO }}
EOM
echo "TOGGL_USER_INFO=$CFGDIR/toggl-user-info.json" >> $GITHUB_OUTPUT
cat > $GAMCFGDIR/gam.cfg <<- EOM
${{ secrets.GAM_CFG }}
EOM
echo "GAMCFGDIR=$GAMCFGDIR" >> $GITHUB_OUTPUT
cat > $GAMCFGDIR/client_secrets.json <<- EOM
${{ secrets.GOOGLE_CREDENTIALS }}
EOM
cat > $GAMCFGDIR/oauth2.txt <<- EOM
${{ secrets.GOOGLE_OAUTH2 }}
EOM
cat > $GAMCFGDIR/oauth2service.json <<- EOM
${{ secrets.GOOGLE_OAUTH2_SERVICE }}
EOM
- name: Lock Toggl time entries
id: lock
env:
TOGGL_API_TOKEN: "${{ secrets.TOGGL_API_TOKEN }}"
TOGGL_WORKSPACE_ID: "${{ secrets.TOGGL_WORKSPACE_ID }}"
run: |
ARGS=""
if [[ -n "${{ github.event.inputs.end-date }}" ]]; then
ARGS="$ARGS --date=${{ github.event.inputs.end-date }}"
fi
compiler-admin time lock $ARGS
- name: Download Toggl time entries
id: download
env:
GAMCFGDIR: "${{ steps.config.outputs.GAMCFGDIR }}"
TOGGL_API_TOKEN: "${{ secrets.TOGGL_API_TOKEN }}"
TOGGL_CLIENT_ID: "${{ secrets.TOGGL_CLIENT_ID }}"
TOGGL_USER_AGENT: "compilerla/compiler-admin"
TOGGL_WORKSPACE_ID: "${{ secrets.TOGGL_WORKSPACE_ID }}"
run: |
ARGS=""
if [[ -n "${{ github.event.inputs.start-date }}" ]]; then
ARGS="$ARGS --start=${{ github.event.inputs.start-date }}"
fi
if [[ -n "${{ github.event.inputs.end-date }}" ]]; then
ARGS="$ARGS --end=${{ github.event.inputs.end-date }}"
fi
OUTPUT=$(compiler-admin time download $ARGS)
echo "$OUTPUT"
FILENAME=$(echo "$OUTPUT" | grep "Download complete:" | cut -d' ' -f3)
echo "filename=$FILENAME" >> $GITHUB_OUTPUT
- name: Convert Toggl entries to Harvest format
id: convert
env:
HARVEST_CLIENT_NAME: "${{ secrets.HARVEST_CLIENT_NAME }}"
GAMCFGDIR: "${{ steps.config.outputs.GAMCFGDIR }}"
TOGGL_PROJECT_INFO: "${{ steps.config.outputs.TOGGL_PROJECT_INFO }}"
TOGGL_USER_INFO: "${{ steps.config.outputs.TOGGL_USER_INFO }}"
run: |
INPUT_FILENAME="${{ steps.download.outputs.filename }}"
OUTPUT_FILENAME=${INPUT_FILENAME/Toggl/Harvest}
compiler-admin time convert --input "$INPUT_FILENAME" --output "$OUTPUT_FILENAME"
echo "filename=$OUTPUT_FILENAME" >> $GITHUB_OUTPUT
- name: Verify time entries
id: verify
env:
HARVEST_CLIENT_NAME: "${{ secrets.HARVEST_CLIENT_NAME }}"
GAMCFGDIR: "${{ steps.config.outputs.GAMCFGDIR }}"
TOGGL_PROJECT_INFO: "${{ steps.config.outputs.TOGGL_PROJECT_INFO }}"
TOGGL_USER_INFO: "${{ steps.config.outputs.TOGGL_USER_INFO }}"
run: |
# First, verify that the files match. This will fail the job if there's a mismatch.
compiler-admin time verify \
"${{ steps.download.outputs.filename }}" \
"${{ steps.convert.outputs.filename }}"
# If verification passes, generate the summary for Slack from just the converted file.
SUMMARY_FULL=$(compiler-admin time verify "${{ steps.convert.outputs.filename }}")
# Truncate the summary to exclude the per-person details.
SUMMARY_TRUNCATED=$(echo "$SUMMARY_FULL" | awk 'BEGIN{RS=""; ORS="\n\n"} NR<=2')
# Format for Slack: add backticks to numbers at the end of a line and escape newlines.
SUMMARY_SLACK=$(echo "$SUMMARY_TRUNCATED" | sed -E 's/: ([0-9.]+)$/: `\1`/' | sed -z 's/\n/\\n/g')
echo "summary=$SUMMARY_SLACK" >> $GITHUB_OUTPUT
- name: Post to Slack
id: slack
if: success()
uses: slackapi/slack-github-action@v2.1.1
with:
method: files.uploadV2
token: ${{ secrets.SLACK_BOT_TOKEN }}
payload: |
channel_id: ${{ secrets.SLACK_CHANNEL_ID }}
initial_comment: "${{ steps.verify.outputs.summary }}"
file: ${{ steps.convert.outputs.filename }}
filename: ${{ steps.convert.outputs.filename }}
- name: Cleanup
id: cleanup
if: always()
run: |
rm -rf .config/