Close a vikunja issue
Find a file
Brad Wenner 4a7a1627a1
All checks were successful
Lint / shellcheck (push) Successful in 3s
fix: prevent substring false positives in reference matching
The contains() check matched /issues/7 against /issues/70 because it
does plain substring matching. Now uses split() with a boundary check
to verify the reference is followed by a non-digit character or end of
string. Prevents closing the wrong task when one issue number is a
prefix of another.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 00:02:55 -07:00
.forgejo/workflows add: vikunja-close action — mark tasks done by ID or reference 2026-04-10 23:41:16 -07:00
action.yml add: vikunja-close action — mark tasks done by ID or reference 2026-04-10 23:41:16 -07:00
CLAUDE.md add: vikunja-close action — mark tasks done by ID or reference 2026-04-10 23:41:16 -07:00
entrypoint.sh fix: prevent substring false positives in reference matching 2026-04-11 00:02:55 -07:00
README.md add: vikunja-close action — mark tasks done by ID or reference 2026-04-10 23:41:16 -07:00

vikunja-close

A Forgejo action that marks a Vikunja task as done. Works by explicit task ID or by searching for a linked reference in the description.

Usage

Close by task ID (same workflow as vikunja-task)

- uses: actions/vikunja-task@main
  id: task
  with:
    title: "${{ github.event.issue.title }}"
    reference: "${{ github.server_url }}/${{ github.repository }}/issues/${{ github.event.issue.number }}"
    project: ${{ secrets.VIKUNJA_PROJECT }}
    api-url: ${{ secrets.VIKUNJA_API_URL }}
    api-token: ${{ secrets.VIKUNJA_API_TOKEN }}

# Later in the same workflow...
- uses: actions/vikunja-close@main
  with:
    task-id: ${{ steps.task.outputs.task-id }}
    api-url: ${{ secrets.VIKUNJA_API_URL }}
    api-token: ${{ secrets.VIKUNJA_API_TOKEN }}

Close by reference (cross-workflow, e.g. on issue close or PR merge)

- uses: actions/vikunja-close@main
  with:
    reference: "${{ github.server_url }}/${{ github.repository }}/issues/${{ github.event.issue.number }}"
    project: ${{ secrets.VIKUNJA_PROJECT }}
    api-url: ${{ secrets.VIKUNJA_API_URL }}
    api-token: ${{ secrets.VIKUNJA_API_TOKEN }}

Close and move to "Done" bucket

- uses: actions/vikunja-close@main
  with:
    reference: "${{ github.server_url }}/${{ github.repository }}/issues/${{ github.event.issue.number }}"
    project: ${{ secrets.VIKUNJA_PROJECT }}
    bucket: Done
    view: Kanban
    api-url: ${{ secrets.VIKUNJA_API_URL }}
    api-token: ${{ secrets.VIKUNJA_API_TOKEN }}

Full issue lifecycle

name: Issue Lifecycle
on:
  issues:
    types: [opened, closed]

jobs:
  create-task:
    if: github.event.action == 'opened'
    runs-on: docker
    container:
      image: alpine:latest
    steps:
      - name: Install tools
        run: apk add --no-cache git curl jq bash
      - name: Checkout
        run: |
          git config --global --add safe.directory "$GITHUB_WORKSPACE"
          git clone "https://ci:${{ github.token }}@git.brads.house/actions/vikunja-task.git" /tmp/vikunja-task
      - uses: /tmp/vikunja-task
        with:
          title: "${{ github.event.issue.title }}"
          description: "${{ github.event.issue.body }}"
          reference: "${{ github.server_url }}/${{ github.repository }}/issues/${{ github.event.issue.number }}"
          project: ${{ secrets.VIKUNJA_PROJECT }}
          api-url: ${{ secrets.VIKUNJA_API_URL }}
          api-token: ${{ secrets.VIKUNJA_API_TOKEN }}

  close-task:
    if: github.event.action == 'closed'
    runs-on: docker
    container:
      image: alpine:latest
    steps:
      - name: Install tools
        run: apk add --no-cache git curl jq bash
      - name: Checkout
        run: |
          git config --global --add safe.directory "$GITHUB_WORKSPACE"
          git clone "https://ci:${{ github.token }}@git.brads.house/actions/vikunja-close.git" /tmp/vikunja-close
      - uses: /tmp/vikunja-close
        with:
          reference: "${{ github.server_url }}/${{ github.repository }}/issues/${{ github.event.issue.number }}"
          project: ${{ secrets.VIKUNJA_PROJECT }}
          api-url: ${{ secrets.VIKUNJA_API_URL }}
          api-token: ${{ secrets.VIKUNJA_API_TOKEN }}

Inputs

Input Required Description
task-id No* Explicit task ID to close
reference No* Forgejo URL or short ref to search for in task descriptions
project No Project name to scope the reference search
bucket No Kanban bucket to move the task to (requires view)
view No Project view name for bucket placement
api-url Yes Vikunja base URL
api-token Yes Vikunja API token

*Must provide either task-id or reference.

Outputs

Output Description
task-id Numeric ID of the closed task
task-url Full URL to the task in the Vikunja web UI
was-already-done "true" if the task was already done before this action ran

Secret cascade

api-url: ${{ secrets.VIKUNJA_API_URL }}    # set once at org level
api-token: ${{ secrets.VIKUNJA_API_TOKEN }}  # set once at org level
project: ${{ secrets.VIKUNJA_PROJECT }}      # override per org or repo

Requirements

Runners must have curl, jq, bash, and git available.