From e8b74e07d147523f8f31ab02ccefef5c4cb660e0 Mon Sep 17 00:00:00 2001 From: Ilyes512 Date: Sun, 2 Aug 2026 15:51:47 +0200 Subject: [PATCH] Initial files added for a go project --- .dockerignore | 21 +++++++ .editorconfig | 18 ++++++ .github/dependabot.yml | 57 +++++++++++++++++++ .github/rulesets/Main.json | 63 +++++++++++++++++++++ .github/workflows/ci.yml | 45 +++++++++++++++ .github/workflows/md.yml | 57 +++++++++++++++++++ .github/workflows/release.yml | 39 +++++++++++++ .gitignore | 7 +++ .golangci.yml | 16 ++++++ .goreleaser.yml | 46 ++++++++++++++++ .markdownlint-cli2.yml | 26 +++++++++ Dockerfile | 60 ++++++++++++++++++++ LICENSE | 21 +++++++ Taskfile.dist.yml | 100 ++++++++++++++++++++++++++++++++++ compose.yml | 99 +++++++++++++++++++++++++++++++++ go.mod | 3 + go.sum | 0 main.go | 7 +++ taskfiles/Taskfile.lint.yml | 17 ++++++ taskfiles/Taskfile.md.yml | 27 +++++++++ 20 files changed, 729 insertions(+) create mode 100644 .dockerignore create mode 100644 .editorconfig create mode 100644 .github/dependabot.yml create mode 100644 .github/rulesets/Main.json create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/md.yml create mode 100644 .github/workflows/release.yml create mode 100644 .gitignore create mode 100644 .golangci.yml create mode 100644 .goreleaser.yml create mode 100644 .markdownlint-cli2.yml create mode 100644 Dockerfile create mode 100644 LICENSE create mode 100644 Taskfile.dist.yml create mode 100644 compose.yml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 taskfiles/Taskfile.lint.yml create mode 100644 taskfiles/Taskfile.md.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..761770e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,21 @@ +.git +.github +.env +.DS_Store +/dist +/labelsync +docs +taskfiles +.dockerignore +.editorconfig +.gitignore +.golangci.yml +.goreleaser.yml +.markdownlint-cli2.yaml +AGENTS.md +CLAUDE.md +compose.yml +Dockerfile +LICENSE +README.md +Taskfile.dist.yml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..89bada9 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[*.{neon,go}] +indent_style = tab diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..b9216f8 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,57 @@ +# Config options: https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: github-actions + directory: / + schedule: + interval: weekly + labels: + - dependencies + - github-actions + commit-message: + prefix: build + include: scope + groups: + specsnl-actions: + patterns: + - "specsnl/github-actions*" + + - package-ecosystem: gomod + directory: / + schedule: + interval: weekly + labels: + - dependencies + - go + commit-message: + prefix: build + include: scope + groups: + go-dependencies: + update-types: + - minor + - patch + + - package-ecosystem: docker + directory: / + schedule: + interval: weekly + labels: + - dependencies + - docker + commit-message: + prefix: build + include: scope + groups: + debian-minor-patch: + patterns: + - debian + update-types: + - minor + - patch + debian-major: + patterns: + - debian + update-types: + - major diff --git a/.github/rulesets/Main.json b/.github/rulesets/Main.json new file mode 100644 index 0000000..e551682 --- /dev/null +++ b/.github/rulesets/Main.json @@ -0,0 +1,63 @@ +{ + "name": "Main", + "target": "branch", + "source_type": "Repository", + "source": "specsnl/labelsync", + "enforcement": "active", + "conditions": { + "ref_name": { + "exclude": [], + "include": [ + "~DEFAULT_BRANCH" + ] + } + }, + "rules": [ + { + "type": "deletion" + }, + { + "type": "non_fast_forward" + }, + { + "type": "pull_request", + "parameters": { + "required_approving_review_count": 0, + "dismiss_stale_reviews_on_push": false, + "required_reviewers": [], + "require_code_owner_review": false, + "dismissal_restriction": { + "enabled": false, + "allowed_actors": [] + }, + "require_last_push_approval": false, + "required_review_thread_resolution": true, + "allowed_merge_methods": [ + "merge" + ] + } + }, + { + "type": "required_status_checks", + "parameters": { + "strict_required_status_checks_policy": false, + "do_not_enforce_on_create": false, + "required_status_checks": [ + { + "context": "Unit tests", + "integration_id": 15368 + }, + { + "context": "Integration tests", + "integration_id": 15368 + }, + { + "context": "Markdown gate", + "integration_id": 15368 + } + ] + } + } + ], + "bypass_actors": [] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..443e79d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + unit-tests: + name: Unit tests + runs-on: ubuntu-24.04 + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up Go + uses: actions/setup-go@v7 + with: + go-version-file: go.mod + + - name: Cache Go modules + uses: actions/cache@v6 + with: + path: | + ~/.cache/go/pkg/mod + ~/.cache/go/build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - name: Run golangci-lint + uses: golangci/golangci-lint-action@v9 + with: + version: v2.12 + + - name: Test + run: go test -race -count=1 ./... + + - name: Build (smoke) + run: go build -o /dev/null . + diff --git a/.github/workflows/md.yml b/.github/workflows/md.yml new file mode 100644 index 0000000..0f8f0d4 --- /dev/null +++ b/.github/workflows/md.yml @@ -0,0 +1,57 @@ +name: Markdown + +on: + workflow_dispatch: + push: + branches: [main] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: read + +jobs: + changes: + name: Changes Filter + runs-on: ubuntu-24.04 + outputs: + mdlint: ${{ steps.filter.outputs.mdlint }} + steps: + - name: Checkout + if: github.event_name != 'pull_request' + uses: actions/checkout@v7 + + - uses: dorny/paths-filter@v4 + id: filter + with: + filters: | + mdlint: + - '**.md' + - '.markdownlint-cli2.yaml' + - '.github/workflows/md.yml' + + mdlint: + name: Check markdown style + runs-on: ubuntu-24.04 + needs: changes + if: ${{ github.event_name == 'workflow_dispatch' || needs.changes.outputs.mdlint == 'true' }} + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Lint Markdown files + uses: DavidAnson/markdownlint-cli2-action@v24 + + gate-mdlint: + name: Markdown gate + runs-on: ubuntu-24.04 + needs: [changes, mdlint] + if: always() + steps: + - run: exit 1 + if: needs.changes.result != 'success' || contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') + - run: exit 0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..72e6264 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,39 @@ +name: Release + +on: + push: + tags: + - "v*" + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - uses: actions/setup-go@v7 + with: + go-version-file: go.mod + + - uses: actions/cache@v6 + with: + path: | + ~/.cache/go/pkg/mod + ~/.cache/go/build + key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ runner.os }}-go- + + - uses: goreleaser/goreleaser-action@v7 + with: + distribution: goreleaser + version: '~> v2' + args: release --clean + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HOMEBREW_TAP_GITHUB_TOKEN: ${{ secrets.HOMEBREW_TAP_GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ab43ac5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.env +.DS_Store +/dev +Taskfile.yml + +/labelsync +/dist/ diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..12d5681 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,16 @@ +version: "2" +linters: + default: standard + enable: + - wsl_v5 + - whitespace + - modernize + # - wrapcheck + settings: + errcheck: + # Output to stdout/stderr is best-effort: the output Writer interface has + # no error channel and reporting a write failure would itself need a + # working stream. See internal/util/output/writer.go. + exclude-functions: + - fmt.Fprintln + - charm.land/lipgloss/v2.Fprintln diff --git a/.goreleaser.yml b/.goreleaser.yml new file mode 100644 index 0000000..727e94d --- /dev/null +++ b/.goreleaser.yml @@ -0,0 +1,46 @@ +version: 2 + +builds: + - id: labelsync + main: . + binary: labelsync + flags: + - -trimpath + - -tags=netgo + ldflags: + - -s -w + - -X github.com/specsnl/labelsync/internal/cmd.Version={{ .Version }} + env: + - CGO_ENABLED=0 + goos: [linux, darwin] + goarch: [amd64, arm64] + +archives: + - formats: [tar.gz] + name_template: "labelsync_{{ .Os }}_{{ .Arch }}{{ if .Arm }}_v{{ .Arm }}{{ end }}" + +checksum: + name_template: "checksums.txt" + +release: + github: + owner: specsnl + name: labelsync + draft: false + prerelease: auto + +homebrew_casks: + - name: labelsync + repository: + owner: specsnl + name: homebrew-tap + token: "{{ .Env.HOMEBREW_TAP_GITHUB_TOKEN }}" + homepage: "https://github.com/specsnl/labelsync" + description: "A Github label synchronization tool" + directory: Casks + hooks: + post: + install: | + if OS.mac? + system_command "/usr/bin/xattr", args: ["-dr", "com.apple.quarantine", "#{staged_path}/labelsync"] + end diff --git a/.markdownlint-cli2.yml b/.markdownlint-cli2.yml new file mode 100644 index 0000000..acb20aa --- /dev/null +++ b/.markdownlint-cli2.yml @@ -0,0 +1,26 @@ +# CLI config: https://github.com/DavidAnson/markdownlint-cli2?tab=readme-ov-file#markdownlint-cli2jsonc +# Lint rules: https://github.com/DavidAnson/markdownlint?tab=readme-ov-file#rules--aliases + +config: + line-length: + line_length: 1000 + tables: false + no-inline-html: + allowed_elements: + - dl + - dt + - dd + - details + - summary + - code + ul-style: + style: dash + # README opens with a centred HTML header and CLAUDE.md with an @import + # directive, so neither starts with a Markdown H1. + first-line-heading: false + +globs: + - '**/*.md' + +gitignore: true +showFound: true diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6c6c810 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,60 @@ +# syntax=docker/dockerfile:1 +# check=error=true + +# Latest version: https://hub.docker.com/_/golang/tags +FROM golang:1.26.5-trixie AS base + +WORKDIR /src + +RUN apt-get update \ + && apt-get install --assume-yes --no-install-recommends \ + ca-certificates \ + tree \ + git \ + openssh-client + +FROM base AS builder-download + +ARG GOARCH=amd64 + +COPY go.mod . +COPY go.sum . + +RUN --mount=type=cache,target=/go/pkg/mod \ + go mod download + +FROM builder-download AS build + +COPY . . + +ARG GOOS=linux +ARG GOARCH=amd64 +ARG GO_MODULE=github.com/specsnl/labelsync +ARG LABELSYNC_VERSION=dev + +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go generate \ + && CGO_ENABLED=0 GOOS=$GOOS GOARCH=$GOARCH go build \ + -trimpath \ + -tags netgo \ + -ldflags "-s -w -X ${GO_MODULE}/internal/cmd.Version=${LABELSYNC_VERSION}" -o ./labelsync + +# Latest version: https://hub.docker.com/_/debian/tags +FROM debian:13.6-slim + +COPY --from=build /src/labelsync /usr/local/bin + +CMD ["labelsync"] + +FROM scratch AS binary + +COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs +COPY --from=build /src/labelsync / +COPY --from=build /etc/passwd /etc/passwd + +CMD ["/labelsync"] + +FROM scratch AS export + +COPY --from=build /src/labelsync /labelsync diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3c6f842 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Specsnl + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Taskfile.dist.yml b/Taskfile.dist.yml new file mode 100644 index 0000000..6fbd322 --- /dev/null +++ b/Taskfile.dist.yml @@ -0,0 +1,100 @@ +# https://taskfile.dev +version: "3" + +includes: + md: ./taskfiles/Taskfile.md.yml + lint: + taskfile: ./taskfiles/Taskfile.lint.yml + flatten: true + +silent: true + +# env: +# BUILDKIT_PROGRESS: plain + +tasks: + + dc: + desc: Run a docker compose command + internal: true + vars: + FIXUID: + sh: echo ${FIXUID:-$(id -u)} + FIXGID: + sh: echo ${FIXGID:-$(id -g)} + cmds: + - FIXUID={{ .FIXUID }} FIXGID={{ .FIXGID }} docker compose {{ .SUB_CMD }} {{ .CLI_ARGS }} + + dc:build: + desc: Run docker compose build + cmds: + - task: dc + vars: + SUB_CMD: --profile=* build + + dc:run:*: + desc: Run a one-off docker compose command + vars: + SERVICE: '{{ index .MATCH 0 }}' + RUN_FLAGS: '{{ .RUN_FLAGS | default "" }}' + USER_FLAG: '{{ (ne (.NON_ROOT | default "true") "false") | ternary " --user $(id -u):$(id -g)" "" }}' + COMPOSE_SERVICES: + sh: COMPOSE_PROFILES="*" docker compose config --services + cmds: + - task: dc + vars: + SUB_CMD: 'run --rm {{ .RUN_FLAGS }} {{ .USER_FLAG }} {{ .SERVICE }} {{ .SUB_CMD }}' + CLI_ARGS: '{{ .CLI_ARGS }}' + requires: + vars: + - name: SERVICE + enum: + ref: .COMPOSE_SERVICES | splitLines | compact + + dc:shell: + desc: Shell into a docker compose service + cmds: + - task: dc + vars: + SUB_CMD: "run --tty --rm go-builder /bin/bash" + CLI_ARGS: "{{.CLI_ARGS}}" + + build: + desc: Build Labelsync + vars: + LABELSYNC_VERSION: + sh: git describe --tags --always --dirty="-dev" 2>/dev/null || echo "dev" + cmds: + - task: lint + - >- + docker buildx bake --file compose.yml + --set "go-binary.output=type=local,dest=." + --set "go-binary.args.GOARCH={{ARCH}}" + --set "go-binary.args.GOOS={{OS}}" + --set "go-binary.args.LABELSYNC_VERSION={{.LABELSYNC_VERSION}}" + go-binary + + test: + desc: Run go tests + cmds: + - task: dc:run:go-builder + vars: + SUB_CMD: "go test -tags=integration ./..." + + release:dry-run: + desc: Build a snapshot release locally without publishing + cmds: + - task: dc:run:goreleaser + vars: + SUB_CMD: release --snapshot --clean + + cleanup: + desc: Cleanup workspace + summary: | + Cleanup of almost all gitignored files, untracked files and development containers. + + The following files/dirs are excluded: none + cmds: + - git clean + -xd + --force diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..896f514 --- /dev/null +++ b/compose.yml @@ -0,0 +1,99 @@ +volumes: + cache: + +services: + volume-init: + image: busybox:1.37.0-musl + user: root + volumes: + - cache:/cache + command: + - sh + - -c + - | + mkdir -p /cache/go-build + mkdir -p /cache/go-mod + mkdir -p /cache/golangci-lint + mkdir -p /cache/npm + chown -R ${FIXUID:-1000}:${FIXGID:-1000} /cache + + go-builder: + profiles: ["build"] + user: ${FIXUID:-1000}:${FIXGID:-1000} + build: + context: . + dockerfile: Dockerfile + target: builder-download + environment: + GOCACHE: /cache/go-build + GOMODCACHE: /cache/go-mod + depends_on: + volume-init: + condition: service_completed_successfully + volumes: + - .:/src + - cache:/cache + + go-binary: + profiles: ["build"] + user: ${FIXUID:-1000}:${FIXGID:-1000} + build: + context: . + dockerfile: Dockerfile + target: export + environment: + GOCACHE: /cache/go-build + GOMODCACHE: /cache/go-mod + depends_on: + volume-init: + condition: service_completed_successfully + volumes: + - cache:/cache + + goreleaser: + profiles: ["build"] + user: ${FIXUID:-1000}:${FIXGID:-1000} + # Latest version: https://hub.docker.com/r/goreleaser/goreleaser/tags + image: goreleaser/goreleaser:v2.15.1 + working_dir: /src + environment: + GOCACHE: /cache/go-build + GOMODCACHE: /cache/go-mod + depends_on: + volume-init: + condition: service_completed_successfully + volumes: + - .:/src + - cache:/cache + + node: + profiles: ["markdown"] + user: ${FIXUID:-1000}:${FIXGID:-1000} + # Latest version: https://hub.docker.com/_/node/tags + image: node:24.15.0-bookworm + working_dir: /src + environment: + NPM_CONFIG_CACHE: /cache/npm + depends_on: + volume-init: + condition: service_completed_successfully + volumes: + - .:/src + - cache:/cache + + golangci-lint: + profiles: ["lint"] + user: ${FIXUID:-1000}:${FIXGID:-1000} + # Latest version: https://hub.docker.com/r/golangci/golangci-lint/tags + image: golangci/golangci-lint:v2.12.2-alpine + working_dir: /src + environment: + GOCACHE: /cache/go-build + GOMODCACHE: /cache/go-mod + GOLANGCI_LINT_CACHE: /cache/golangci-lint + depends_on: + volume-init: + condition: service_completed_successfully + volumes: + - .:/src + - cache:/cache diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..7099705 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/specsnl/labelsync + +go 1.26.5 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/main.go b/main.go new file mode 100644 index 0000000..1c4c43f --- /dev/null +++ b/main.go @@ -0,0 +1,7 @@ +package main + +import "fmt" + +func main() { + fmt.Println("Nothing to see yet!") +} diff --git a/taskfiles/Taskfile.lint.yml b/taskfiles/Taskfile.lint.yml new file mode 100644 index 0000000..9cb7c98 --- /dev/null +++ b/taskfiles/Taskfile.lint.yml @@ -0,0 +1,17 @@ +# https://taskfile.dev +version: "3" + +tasks: + lint: + desc: Run golangci-lint + cmds: + - task: dc:run:golangci-lint + vars: + SUB_CMD: "golangci-lint run" + + lint:fix: + desc: Run golangci-lint + cmds: + - task: dc:run:golangci-lint + vars: + SUB_CMD: "golangci-lint run --fix" diff --git a/taskfiles/Taskfile.md.yml b/taskfiles/Taskfile.md.yml new file mode 100644 index 0000000..ee8df09 --- /dev/null +++ b/taskfiles/Taskfile.md.yml @@ -0,0 +1,27 @@ +# https://taskfile.dev +version: "3" + +tasks: + + check: + desc: Check the style of Markdown files with markdownlint + cmds: + - task: :dc:run:node + vars: + SUB_CMD: "npx --yes markdownlint-cli2 {{.SUB_CMD}}" + + fix: + desc: Fix Markdown style — align tables, then apply autofixable lint rules + cmds: + - task: fix-tables + - task: check + vars: + SUB_CMD: --fix + + fix-tables: + desc: Align table spacing in Markdown files + cmds: + # dotglob so hidden directories (e.g. .github) are covered too. + - task: :dc:run:node + vars: + SUB_CMD: bash -c "shopt -s globstar dotglob && npx --yes markdown-table-formatter **/*.md"