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
17 changes: 15 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,27 @@ DDEV workflow.
- Provides **global** commands (available in all projects, project level commands below take priority):
- - [`branch`](commands/host/branch): Creates an opinionated git branch name from a Teamwork ticket ID
- - [`cloudflare`](commands/host/cloudflare): Shares the project with the outside world over a Cloudflare tunnel
- - [`comment`](commands/host/comment): Post a comment to a Teamwork task
- - [`drupal-updater`](commands/host/drupal-updater): Automatically updates Core and Contrib. Usage `drupal-updater -cugado`.
- - [`login`](commands/host/login): Opens a browser and logs you in to Drupal (works on local environments only)
- - [`open-issue`](commands/host/open-issue): Opens teamwork issue for current branch
- - [`timeslip`](commands/host/timeslip): Generates a timeslip message for FreeAgent. If `timewarrior` is installed it will also show the sum of time spent today
- - [`timelog`](commands/host/timelog): Log time to a Teamwork task
- - [`timew`](commands/host/timew): Tags current timewarrior tracking with Teamwork link and project name
- - [`travel-mode`](commands/host/travel-mode): Removes all DB dumps
downloaded via `ddev pull`, also provides info on how to remove all DDEV
projects and their databases
- Provides **host** commands:
- - [`branch`](commands/host/branch): Creates an opinionated git branch name from a Teamwork ticket ID
- - [`cloudflare`](commands/host/cloudflare): Shares the project with the outside world over a Cloudflare tunnel
- - [`comment`](commands/host/comment): Post a comment to a Teamwork task
- - [`devmode [on|off]`](commands/host/devmode): Adds custom settings.local.php file and allows easy toggle between production and development mode
- - [`drupal-updater`](commands/host/drupal-updater): Automatically updates Core and Contrib. Usage `drupal-updater -cugado`.
- - [`githooks`](commands/host/githooks): Installs git-hooks (also happens on project start)
- - [`lints`](commands/host/lints): Shows available linters and the way to run them
- - [`login`](commands/host/login): Opens a browser and logs you in to Drupal (works on local environments only)
- - [`protect [on|off|reset]`](commands/host/protect): Enable or disable basic auth on a nixsal hosted dev project - [see file](commands/host/protect)
- - [`tests`](commands/host/tests): Informs about available tests for current project
- - [`timeslip`](commands/host/timeslip): Generates a timeslip message for FreeAgent. If `timewarrior` is installed it will also show the sum of time spent today
- - [`timelog`](commands/host/timelog): Log time to a Teamwork task
- Provides **web container** commands:
- - [`behat`](commands/web/behat): Runs behat
- - [`install-varnish`](commands/web/install-varnish): Installs and configures Varnish on platform.sh project. See [Varnish command README](scripts/varnish/README.md)
Expand Down Expand Up @@ -113,6 +115,17 @@ if (isset($platformsh->branch)) {
</code></pre>
</details>

## Teamwork Integration

Several commands integrate with Teamwork (`comment`, `timelog`, `open-issue`, `timew`). These require the following environment variables to be set on your host machine:

```bash
export TEAMWORK_DOMAIN="projects.yourcompany.com"
export TEAMWORK_API_KEY="your_api_key_here"
```

Add these to your `~/.bashrc` or `~/.zshrc` file.

## Tricks

Handy shell aliases to add to your **host** machine:
Expand Down
105 changes: 105 additions & 0 deletions commands/host/comment
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
#ddev-generated
#annertech-ddev
#teamwork-api

## Description: Post a comment to a Teamwork task
## Usage: comment [-v] [-t task_id] "<comment>"
## Example: ddev comment "Fixed the issue"
## Example: ddev comment -t 12345678 "Comment on specific task"
## Aliases: comment

VERBOSE=false
TASK_ID_OVERRIDE=""

# Parse flags
while [[ "$1" == -* ]]; do
case "$1" in
-v)
VERBOSE=true
echo "Verbose mode enabled."
shift
;;
-t)
TASK_ID_OVERRIDE="$2"
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done

COMMENT=$1

API_KEY="${TEAMWORK_API_KEY}"
DOMAIN="${TEAMWORK_DOMAIN}"

if [[ -z "$API_KEY" || -z "$DOMAIN" ]]; then
echo "Error: TEAMWORK_API_KEY and TEAMWORK_DOMAIN environment variables must be set."
exit 1
fi

# Use override if provided, otherwise extract from branch
if [[ -n "$TASK_ID_OVERRIDE" ]]; then
TASK_ID="$TASK_ID_OVERRIDE"
else
branch_name=$(git rev-parse --abbrev-ref HEAD)
if [[ $branch_name =~ T-([0-9]+) ]]; then
TASK_ID="${BASH_REMATCH[1]}"
else
echo "No task ID found in the branch name and none provided via -t flag."
echo "Either use a branch containing 'T-<task_id>' or provide -t <task_id>"
exit 1
fi
fi

if [[ -z "$COMMENT" ]]; then
echo "Usage: ddev comment [-v] [-t task_id] \"<comment>\""
exit 1
fi

# Construct JSON payload
# Teamwork API expects 'comment' wrapper
# content-type can be TEXT or HTML. Using TEXT for simplicity.
DATA=$(cat <<EOF
{
"comment": {
"body": "$COMMENT",
"content-type": "TEXT"
}
}
EOF
)

URL="https://${DOMAIN}/tasks/${TASK_ID}/comments.json"

if [ "$VERBOSE" = true ]; then
echo "Posting comment to Task ${TASK_ID}..."
echo "Using Domain: '${DOMAIN}'"
echo "Constructed URL: $URL"
echo "Payload: $DATA"

# Execute Request with verbose output
curl -v \
-u "${API_KEY}:xxx" \
-H "Content-Type: application/json" \
-X POST "$URL" \
-d "$DATA"
echo "" # Newline for clean output
else
# Execute Request silently
RESPONSE=$(curl -s -w "%{http_code}" -o /dev/null \
-u "${API_KEY}:xxx" \
-H "Content-Type: application/json" \
-X POST "$URL" \
-d "$DATA")

if [[ "$RESPONSE" =~ ^2 ]]; then
echo "Done."
echo "https://${DOMAIN}/app/tasks/${TASK_ID}"
else
echo "Error: API returned HTTP $RESPONSE"
fi
fi
9 changes: 8 additions & 1 deletion commands/host/open-issue
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,20 @@
## Example: "ddev open-issue"
## Installed globally

DOMAIN="${TEAMWORK_DOMAIN}"

if [[ -z "$DOMAIN" ]]; then
echo "Error: TEAMWORK_DOMAIN environment variable must be set."
exit 1
fi

# Get the name of the active Git branch
branch_name=$(git rev-parse --abbrev-ref HEAD)

# Extract the task ID using regex
if [[ $branch_name =~ T-([0-9]+) ]]; then
task_id="${BASH_REMATCH[1]}"
result="https://projects.annertech.com/app/tasks/$task_id"
result="https://${DOMAIN}/app/tasks/$task_id"

# Copy to clipboard based on OS
if command -v xclip &> /dev/null; then
Expand Down
156 changes: 156 additions & 0 deletions commands/host/timelog
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#!/usr/bin/env bash
#ddev-generated
#annertech-ddev
#teamwork-api

## Description: Log time to a Teamwork task
## Usage: timelog [-v] [-u] [-t task_id] <time> "<comment>"
## Example: ddev timelog 1h15m "Code review"
## Example: ddev timelog -u 30m "Internal meeting (unbillable)"
## Aliases: timelog, timeslip

VERBOSE=false
TASK_ID_OVERRIDE=""
BILLABLE="true"

# Parse flags
while [[ "$1" == -* ]]; do
case "$1" in
-v)
VERBOSE=true
echo "Verbose mode enabled."
shift
;;
-u)
BILLABLE="false"
shift
;;
-t)
TASK_ID_OVERRIDE="$2"
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done

TIME_INPUT=$1
COMMENT=$2

if [[ -z "$TIME_INPUT" || -z "$COMMENT" ]]; then
echo "Usage: ddev timelog [-v] [-u] [-t task_id] <time> \"<comment>\""
echo "Time formats: 1h15m, 75m, 1.25 (decimal hours), 75 (plain minutes)"
exit 1
fi

# Parse time input into total minutes
# Supports: "1h15m", "75m", "1.25" (decimal hours), "75" (plain minutes)
parse_time() {
local input="$1"
local total=0

if [[ "$input" =~ ^([0-9]+)h([0-9]+)m$ ]]; then
# Format: 1h15m
total=$(( ${BASH_REMATCH[1]} * 60 + ${BASH_REMATCH[2]} ))
elif [[ "$input" =~ ^([0-9]+)h$ ]]; then
# Format: 1h
total=$(( ${BASH_REMATCH[1]} * 60 ))
elif [[ "$input" =~ ^([0-9]+)m$ ]]; then
# Format: 75m
total=${BASH_REMATCH[1]}
elif [[ "$input" =~ ^([0-9]+)\.([0-9]+)$ ]]; then
# Format: 1.25 (decimal hours)
local decimal="${BASH_REMATCH[1]}.${BASH_REMATCH[2]}"
total=$(echo "$decimal * 60" | bc | cut -d. -f1)
elif [[ "$input" =~ ^[0-9]+$ ]]; then
# Format: 75 (plain minutes)
total=$input
else
echo "Invalid time format: $input"
echo "Supported formats: 1h15m, 75m, 1.25 (decimal hours), 75 (plain minutes)"
exit 1
fi

echo "$total"
}

TOTAL_MINUTES=$(parse_time "$TIME_INPUT")

API_KEY="${TEAMWORK_API_KEY}"
DOMAIN="${TEAMWORK_DOMAIN}"

if [[ -z "$API_KEY" || -z "$DOMAIN" ]]; then
echo "Error: TEAMWORK_API_KEY and TEAMWORK_DOMAIN environment variables must be set."
exit 1
fi

# Use override if provided, otherwise extract from branch
if [[ -n "$TASK_ID_OVERRIDE" ]]; then
TASK_ID="$TASK_ID_OVERRIDE"
else
branch_name=$(git rev-parse --abbrev-ref HEAD)
if [[ $branch_name =~ T-([0-9]+) ]]; then
TASK_ID="${BASH_REMATCH[1]}"
else
echo "No task ID found in the branch name and none provided via -t flag."
echo "Either use a branch containing 'T-<task_id>' or provide -t <task_id>"
exit 1
fi
fi

# Calculate hours and remaining minutes
HOURS=$((TOTAL_MINUTES / 60))
MINS=$((TOTAL_MINUTES % 60))

# Get current date and time
DATE=$(date +%Y%m%d)
TIME=$(date +%H:%M)

# Construct JSON payload
# Teamwork API V1 expects 'time-entry' wrapper
DATA=$(cat <<EOF
{
"time-entry": {
"description": "$COMMENT",
"date": "$DATE",
"time": "$TIME",
"hours": "$HOURS",
"minutes": "$MINS",
"isbillable": "$BILLABLE"
}
}
EOF
)

URL="https://${DOMAIN}/tasks/${TASK_ID}/time_entries.json"

if [ "$VERBOSE" = true ]; then
echo "Logging ${HOURS}h ${MINS}m to Task ${TASK_ID}..."
echo "Using Domain: '${DOMAIN}'"
echo "Constructed URL: $URL"
echo "Payload: $DATA"

# Execute Request with verbose output
curl -v \
-u "${API_KEY}:xxx" \
-H "Content-Type: application/json" \
-X POST "$URL" \
-d "$DATA"
echo "" # Newline for clean output
else
# Execute Request silently
RESPONSE=$(curl -s -w "%{http_code}" -o /dev/null \
-u "${API_KEY}:xxx" \
-H "Content-Type: application/json" \
-X POST "$URL" \
-d "$DATA")

if [[ "$RESPONSE" =~ ^2 ]]; then
echo "Done."
echo "https://${DOMAIN}/app/tasks/${TASK_ID}"
else
echo "Error: API returned HTTP $RESPONSE"
fi
fi
56 changes: 0 additions & 56 deletions commands/host/timeslip

This file was deleted.

Loading