Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f8bc389ae5 | |||
| 72efcb13ba | |||
| 096216579d | |||
| d692fa5a4c |
74
.gitea/workflows/release.yml
Normal file
74
.gitea/workflows/release.yml
Normal file
@@ -0,0 +1,74 @@
|
||||
name: Release Binaries
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- arch: amd64
|
||||
platform: linux/amd64
|
||||
image: golang:1.24-alpine
|
||||
runner: linux-amd64
|
||||
upx_install: "apk add --no-cache upx"
|
||||
- arch: arm64
|
||||
platform: linux/arm64
|
||||
image: golang:1.24-alpine
|
||||
runner: linux-arm64
|
||||
upx_install: "apk add --no-cache upx zstd-static"
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build (${{ matrix.arch }})
|
||||
run: |
|
||||
set -o pipefail
|
||||
|
||||
VERSION="$(git describe --tags --always --dirty)"
|
||||
echo "Version: $VERSION"
|
||||
|
||||
# Crea il container senza avviarlo
|
||||
CID=$(docker create \
|
||||
--platform ${{ matrix.platform }} \
|
||||
-w /src \
|
||||
${{ matrix.image }} \
|
||||
sleep infinity)
|
||||
|
||||
# Copia i sorgenti dentro con docker cp (funziona anche in DinD)
|
||||
docker cp "${{ github.workspace }}/." "$CID:/src"
|
||||
|
||||
# Avvia ed esegui la build
|
||||
docker start "$CID"
|
||||
docker exec "$CID" sh -c "
|
||||
${{ matrix.upx_install }}
|
||||
export CGO_ENABLED=0
|
||||
go build -trimpath -ldflags='-s -w -X main.version=$VERSION' -o /src/dist/todotxt2remind .
|
||||
upx /src/dist/todotxt2remind
|
||||
" 2>&1 | cat
|
||||
|
||||
# Copia il binario fuori
|
||||
docker cp "$CID:/src/dist/todotxt2remind" \
|
||||
"${{ github.workspace }}/todotxt2remind-linux-${{ matrix.arch }}"
|
||||
|
||||
docker rm -f "$CID"
|
||||
|
||||
- name: Carica artefatto sulla release
|
||||
env:
|
||||
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
RELEASE_ID: ${{ github.event.release.id }}
|
||||
run: |
|
||||
FILENAME="todotxt2remind-linux-${{ matrix.arch }}"
|
||||
curl -s -X POST \
|
||||
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${RELEASE_ID}/assets?name=${FILENAME}" \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary "@${FILENAME}"
|
||||
@@ -44,10 +44,10 @@ func (t Task) ToRemind(indent int) string {
|
||||
// k must have the first letter uppercase for Remind
|
||||
kUpper := strings.ToUpper(k[:1]) + k[1:]
|
||||
if len(values) == 1 {
|
||||
sb.WriteString(fmt.Sprintf("INFO \"%s: %s\" ", kUpper, values[0]))
|
||||
fmt.Fprintf(&sb, "INFO \"%s: %s\" ", kUpper, values[0])
|
||||
} else {
|
||||
for i, v := range values {
|
||||
sb.WriteString(fmt.Sprintf("INFO \"%s%d: %s\" ", kUpper, i+1, v))
|
||||
fmt.Fprintf(&sb, "INFO \"%s%d: %s\" ", kUpper, i+1, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,22 +55,22 @@ func (t Task) ToRemind(indent int) string {
|
||||
|
||||
if len(t.Projects) > 0 {
|
||||
for _, p := range t.Projects {
|
||||
sb.WriteString(fmt.Sprintf("INFO \"List: %s\" ", p))
|
||||
fmt.Fprintf(&sb, "INFO \"List: %s\" ", p)
|
||||
}
|
||||
}
|
||||
|
||||
if len(t.Contexts) > 0 {
|
||||
for _, c := range t.Contexts {
|
||||
sb.WriteString(fmt.Sprintf("INFO \"Tag: %s\" ", c))
|
||||
fmt.Fprintf(&sb, "INFO \"Tag: %s\" ", c)
|
||||
}
|
||||
}
|
||||
|
||||
if t.Completed {
|
||||
sb.WriteString(fmt.Sprintf("COMPLETE-THROUGH %s ", t.DueDate.Format("2006-01-02")))
|
||||
fmt.Fprintf(&sb, "COMPLETE-THROUGH %s ", t.DueDate.Format("2006-01-02"))
|
||||
}
|
||||
|
||||
if t.Priority != nil {
|
||||
sb.WriteString(fmt.Sprintf("PRIORITY %d ", t.PriorityAsRemind()))
|
||||
fmt.Fprintf(&sb, "PRIORITY %d ", t.PriorityAsRemind())
|
||||
}
|
||||
|
||||
sb.WriteString("MSG")
|
||||
@@ -79,7 +79,7 @@ func (t Task) ToRemind(indent int) string {
|
||||
sb.WriteString(" %<List>:")
|
||||
}
|
||||
|
||||
sb.WriteString(fmt.Sprintf(" %s", t.Description))
|
||||
fmt.Fprintf(&sb, " %s", t.Description)
|
||||
|
||||
if t.Completed {
|
||||
sb.WriteString("%:")
|
||||
@@ -97,11 +97,11 @@ func (t Task) ToRemind(indent int) string {
|
||||
// uppercase first letter for Remind
|
||||
kUpper := strings.ToUpper(k[:1]) + k[1:]
|
||||
if len(values) == 1 {
|
||||
sb.WriteString(fmt.Sprintf("%%_%s%s: %%<%s>", headingSpaces, kUpper, kUpper))
|
||||
fmt.Fprintf(&sb, "%%_%s%s: %%<%s>", headingSpaces, kUpper, kUpper)
|
||||
} else {
|
||||
for i := range values {
|
||||
kNumbered := fmt.Sprintf("%s%d", kUpper, i+1)
|
||||
sb.WriteString(fmt.Sprintf("%%_%s%s: %%<%s>", headingSpaces, kNumbered, kNumbered))
|
||||
fmt.Fprintf(&sb, "%%_%s%s: %%<%s>", headingSpaces, kNumbered, kNumbered)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -202,6 +202,7 @@ func ParseLine(line string) (Task, error) {
|
||||
working := strings.TrimSpace(line)
|
||||
toks := splitTokens(working)
|
||||
i := 0
|
||||
var invalidDue string
|
||||
|
||||
// 1) Completed?
|
||||
if i < len(toks) && toks[i] == "x" {
|
||||
@@ -230,14 +231,8 @@ func ParseLine(line string) (Task, error) {
|
||||
i++
|
||||
}
|
||||
|
||||
// 5) If not completed, check for priority at start
|
||||
// 5) Creation date for incomplete tasks (priority, if any, was already consumed in step 4)
|
||||
if !t.Completed {
|
||||
if i < len(toks) && len(toks[i]) == 3 && toks[i][0] == '(' && toks[i][2] == ')' && toks[i][1] >= 'A' && toks[i][1] <= 'Z' {
|
||||
r := rune(toks[i][1])
|
||||
t.Priority = &r
|
||||
i++
|
||||
}
|
||||
// Creation date for incomplete tasks
|
||||
if i < len(toks) && dateRe.MatchString(toks[i]) {
|
||||
if dt, err := time.Parse(dateLayout, toks[i]); err == nil {
|
||||
t.CreationDate = &dt
|
||||
@@ -285,9 +280,12 @@ func ParseLine(line string) (Task, error) {
|
||||
k, v := parts[0], parts[1]
|
||||
if k != "" && v != "" && !isProtocolKey(k) {
|
||||
t.Metadata[k] = append(t.Metadata[k], v)
|
||||
if k == "due" && dateRe.MatchString(v) {
|
||||
if dt, err := time.Parse(dateLayout, v); err == nil {
|
||||
if k == "due" {
|
||||
dt, err := time.Parse(dateLayout, v)
|
||||
if dateRe.MatchString(v) && err == nil {
|
||||
t.DueDate = &dt
|
||||
} else {
|
||||
invalidDue = v
|
||||
}
|
||||
}
|
||||
continue
|
||||
@@ -302,6 +300,10 @@ func ParseLine(line string) (Task, error) {
|
||||
return t, errors.New("completed task missing completion date (spec requires completion date directly after 'x')")
|
||||
}
|
||||
|
||||
if invalidDue != "" {
|
||||
return t, fmt.Errorf("invalid due date %q: expected format YYYY-MM-DD (task will be dropped, no Remind entry generated)", invalidDue)
|
||||
}
|
||||
|
||||
return t, nil
|
||||
}
|
||||
|
||||
|
||||
2
main.go
2
main.go
@@ -15,7 +15,7 @@ var (
|
||||
outputFile string
|
||||
debug bool
|
||||
indent int
|
||||
version = "v3.0.0"
|
||||
version = "dev" // overridden at build time via -ldflags "-X main.version=..."
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
@@ -73,6 +73,9 @@ X 2012-01-01 Make resolutions
|
||||
(A) 2024-13-99 Call Mom
|
||||
x 2024-99-99 Call Mom
|
||||
|
||||
# Malformed: due date with invalid format (should error, task dropped from output)
|
||||
(A) Call Mom due:2024-13-99
|
||||
|
||||
# Malformed: key:value with whitespace in key or value
|
||||
foo bar:baz
|
||||
foo:bar baz
|
||||
|
||||
Reference in New Issue
Block a user