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
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on:
pull_request:
push:
branches:
- master

permissions:
contents: read

jobs:
check:
name: Check
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true

- name: Run checks
run: make check
71 changes: 71 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: Release

on:
push:
tags:
- "v*"

permissions:
contents: write

jobs:
release:
name: Build and release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true

- name: Verify
run: make check

- name: Build archives
env:
VERSION: ${{ github.ref_name }}
run: |
set -euo pipefail
mkdir -p dist
for target in \
linux/amd64 \
linux/arm64 \
darwin/amd64 \
darwin/arm64 \
windows/amd64 \
windows/arm64
do
GOOS="${target%/*}"
GOARCH="${target#*/}"
name="redmine_${VERSION}_${GOOS}_${GOARCH}"
binary="redmine"
if [ "$GOOS" = "windows" ]; then
binary="redmine.exe"
fi
mkdir -p "dist/$name"
CGO_ENABLED=0 GOOS="$GOOS" GOARCH="$GOARCH" go build \
-trimpath \
-ldflags "-s -w -X main.version=${VERSION}" \
-o "dist/$name/$binary" \
./cmd/redmine
cp README.md LICENSE "dist/$name/"
if [ "$GOOS" = "windows" ]; then
(cd dist && zip -qr "$name.zip" "$name")
else
tar -C dist -czf "dist/$name.tar.gz" "$name"
fi
rm -rf "dist/$name"
done
(cd dist && sha256sum * > checksums.txt)

- name: Publish GitHub Release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ github.ref_name }}
run: gh release create "$VERSION" dist/* --title "$VERSION" --generate-notes
8 changes: 5 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
bin/
dist/
*.exe
*.exe~
*.dll
Expand All @@ -18,7 +20,7 @@ coverage.*
profile.cov

# Dependency directories (remove the comment below to include it)
# vendor/
vendor/

# Go workspace file
go.work
Expand All @@ -28,5 +30,5 @@ go.work.sum
.env

# Editor/IDE
# .idea/
# .vscode/
.idea/
.vscode/
26 changes: 26 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Redmine CLI

## Development

* Cover implementation changes with tests
* Add and generate documentation when adding or changing functionality
* At the end of implementation, run `make fix test gen-docs`

## Running Individual Checks

```bash
make build # compile
make lint # golangci-lint
make fix # auto-fix lint issues (gofmt + goimports)
make test # all unit tests
make gen # regenerate OpenAPI command metadata and docs
make gen-docs # regenerate docs
make check # lint, test, and generated-file checks
make check-docs # verify generated docs are current
```

## Documentation conventions

CLI documentation is generated from `openapi/openapi.yaml` by `make gen-docs`.

Use English as the primary language for documentation, commits, comments, etc.
1 change: 1 addition & 0 deletions CLAUDE.md
84 changes: 84 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
OPENAPI_SPEC_URL ?= https://raw.githubusercontent.com/d-yoshi/redmine-openapi/main/openapi.yaml
OPENAPI_SPEC_DIR ?= openapi
OPENAPI_SPEC ?= $(OPENAPI_SPEC_DIR)/openapi.yaml
GO ?= go
BINARY ?= redmine
BIN_DIR ?= bin
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo dev)
LDFLAGS ?= -s -w -X main.version=$(VERSION)
GOFILES := $(shell find . -name '*.go' -not -path './vendor/*')

.DEFAULT_GOAL := help

.PHONY: help download-openapi-spec build install lint fix test gen gen-docs check-docs check-gen check clean
help:
@echo "Targets:"
@echo " download-openapi-spec Download latest Redmine OpenAPI spec to $(OPENAPI_SPEC)"
@echo " build Compile $(BINARY)"
@echo " install Install $(BINARY)"
@echo " lint Run golangci-lint when available, otherwise go vet"
@echo " fix Run gofmt and goimports"
@echo " test Run unit tests"
@echo " gen Regenerate OpenAPI command metadata and docs"
@echo " gen-docs Regenerate docs/usage.md and generated metadata"
@echo " check-docs Verify docs/usage.md is current"
@echo " check-gen Verify generated files are current"
@echo " check Run lint, tests, and generated-file checks"

download-openapi-spec:
@mkdir -p "$(dir $(OPENAPI_SPEC))"
@tmp=$$(mktemp); \
trap 'rm -f "$$tmp"' EXIT; \
curl -fsSL "$(OPENAPI_SPEC_URL)" -o "$$tmp"; \
grep -q '^openapi: 3\.' "$$tmp"; \
mv "$$tmp" "$(OPENAPI_SPEC)"; \
echo "Downloaded $(OPENAPI_SPEC)"

build:
@mkdir -p "$(BIN_DIR)"
$(GO) build -trimpath -ldflags "$(LDFLAGS)" -o "$(BIN_DIR)/$(BINARY)" ./cmd/redmine

install:
$(GO) install -trimpath -ldflags "$(LDFLAGS)" ./cmd/redmine

lint:
@if command -v golangci-lint >/dev/null 2>&1; then \
golangci-lint run; \
else \
echo "golangci-lint not found; running go vet"; \
$(GO) vet ./...; \
fi

fix:
@gofmt -w $(GOFILES)
@if command -v goimports >/dev/null 2>&1; then \
goimports -w $(GOFILES); \
else \
$(GO) run golang.org/x/tools/cmd/goimports@latest -w $(GOFILES); \
fi

test:
$(GO) test ./...

gen:
$(GO) run ./cmd/redmine-openapi-gen -spec "$(OPENAPI_SPEC)" -out internal/openapi/generated.go -docs docs/usage.md

gen-docs: gen

check-docs:
@tmp=$$(mktemp -d); \
trap 'rm -rf "$$tmp"' EXIT; \
$(GO) run ./cmd/redmine-openapi-gen -spec "$(OPENAPI_SPEC)" -out "$$tmp/generated.go" -docs "$$tmp/usage.md"; \
diff -u docs/usage.md "$$tmp/usage.md"

check-gen:
@tmp=$$(mktemp -d); \
trap 'rm -rf "$$tmp"' EXIT; \
$(GO) run ./cmd/redmine-openapi-gen -spec "$(OPENAPI_SPEC)" -out "$$tmp/generated.go" -docs "$$tmp/usage.md"; \
diff -u internal/openapi/generated.go "$$tmp/generated.go"; \
diff -u docs/usage.md "$$tmp/usage.md"

check: lint test check-gen

clean:
rm -rf "$(BIN_DIR)" dist
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,26 @@
# redmine-cli
A Redmine CLI tool bringing Redmine to your command line

A Redmine CLI tool bringing Redmine to your command line.

Built based on https://d-yoshi.github.io/redmine-openapi/ ([repo](https://github.com/d-yoshi/redmine-openapi)).

## Usage

```bash
redmine auth login --host https://redmine.example.com --api-key "$REDMINE_API_KEY"
redmine issue list --limit 20
redmine issue show 123 --include journals
redmine issue create --project-id my-project --subject "Fix checkout"
```

Full generated usage documentation is in [docs/usage.md](docs/usage.md).

## Development

The command metadata and usage docs are generated from `openapi/openapi.yaml`.

```bash
make download-openapi-spec
make gen
make check
```
Loading