diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index fc262ce..ab31c03 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,19 +1,10 @@ { "name": "Copia CLI", - "image": "mcr.microsoft.com/devcontainers/base:ubuntu", - "features": { - "ghcr.io/devcontainers/features/go:1": { - "version": "1.26.1", - "golangciLintVersion": "latest" - }, - "ghcr.io/devcontainers/features/github-cli:1": {}, - "ghcr.io/devcontainers/features/node:1": { - "version": "lts" - } - }, - "mounts": [ - "source=${localEnv:HOME}/.claude,target=/home/vscode/.claude,type=bind,consistency=cached" - ], + "dockerComposeFile": ["../docker-compose.yml", "../docker-compose.dev.yaml"], + "service": "go", + "workspaceFolder": "/app", + "postCreateCommand": "git config --global --add safe.directory /app && go mod download", + "forwardPorts": [], "customizations": { "vscode": { "extensions": [ @@ -24,9 +15,9 @@ "go.toolsManagement.autoUpdate": true, "go.testOnSave": true, "go.lintTool": "golangci-lint", - "editor.formatOnSave": true + "editor.formatOnSave": true, + "security.workspace.trust.startupPrompt": "never" } } - }, - "postCreateCommand": "npm install -g @anthropic-ai/claude-code && ([ -f go.sum ] && go mod download || true)" + } } diff --git a/.envrc.example b/.envrc.example new file mode 100644 index 0000000..b804e0b --- /dev/null +++ b/.envrc.example @@ -0,0 +1,5 @@ +export COMPOSE_PROJECT_NAME=copia-cli + +# Copia API credentials (for integration tests) +# export COPIA_TOKEN=your-token-here +# export COPIA_HOST=https://app.copia.io diff --git a/.github/workflows/aur.yml b/.github/workflows/aur.yml new file mode 100644 index 0000000..bc9cedc --- /dev/null +++ b/.github/workflows/aur.yml @@ -0,0 +1,63 @@ +name: Publish to AUR + +on: + release: + types: [released] + +jobs: + aur: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Extract version + id: version + run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" + + - name: Get checksums + id: checksums + run: | + gh release download "${{ github.ref_name }}" --pattern "checksums.txt" --output checksums.txt + echo "sha256_x86_64=$(grep 'linux_amd64.tar.gz' checksums.txt | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + echo "sha256_aarch64=$(grep 'linux_arm64.tar.gz' checksums.txt | cut -d' ' -f1)" >> "$GITHUB_OUTPUT" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Generate PKGBUILD + run: | + VERSION=${{ steps.version.outputs.version }} + SHA_X86=${{ steps.checksums.outputs.sha256_x86_64 }} + SHA_ARM=${{ steps.checksums.outputs.sha256_aarch64 }} + cat > PKGBUILD < + pkgname=copia-cli-bin + pkgver=${VERSION} + pkgrel=1 + pkgdesc="CLI for Copia — source control for industrial automation" + arch=('x86_64' 'aarch64') + url="https://github.com/qubernetic/copia-cli" + license=('AGPL-3.0-only') + provides=('copia-cli') + conflicts=('copia-cli') + + source_x86_64=("\${url}/releases/download/v\${pkgver}/copia-cli_\${pkgver}_linux_amd64.tar.gz") + source_aarch64=("\${url}/releases/download/v\${pkgver}/copia-cli_\${pkgver}_linux_arm64.tar.gz") + sha256sums_x86_64=('${SHA_X86}') + sha256sums_aarch64=('${SHA_ARM}') + + package() { + install -Dm755 copia-cli "\${pkgdir}/usr/bin/copia-cli" + install -Dm644 LICENSE "\${pkgdir}/usr/share/licenses/\${pkgname}/LICENSE" + } + PKGEOF + sed -i 's/^ //' PKGBUILD + + - name: Publish to AUR + uses: KSXGitHub/github-actions-deploy-aur@v3 + with: + pkgname: copia-cli-bin + pkgbuild: ./PKGBUILD + commit_username: qubernetic + commit_email: info@qubernetic.com + ssh_private_key: ${{ secrets.AUR_SSH_PRIVATE_KEY }} + commit_message: "Update to ${{ steps.version.outputs.version }}" diff --git a/.github/workflows/bump-go.yml b/.github/workflows/bump-go.yml index 2f6bf38..d898689 100644 --- a/.github/workflows/bump-go.yml +++ b/.github/workflows/bump-go.yml @@ -17,6 +17,10 @@ jobs: with: ref: develop + - uses: actions/setup-go@v6 + with: + go-version: stable + - name: Check latest Go version id: check run: | @@ -30,11 +34,12 @@ jobs: echo "needs_update=false" >> "$GITHUB_OUTPUT" fi - - name: Update go.mod + - name: Update Go version if: steps.check.outputs.needs_update == 'true' run: | go mod edit -go=${{ steps.check.outputs.latest }} go mod tidy + sed -i "s|^FROM golang:.*-alpine|FROM golang:${{ steps.check.outputs.latest }}-alpine|" Dockerfile - name: Create Pull Request if: steps.check.outputs.needs_update == 'true' @@ -44,8 +49,9 @@ jobs: commit-message: "chore(deps): bump Go from ${{ steps.check.outputs.current }} to ${{ steps.check.outputs.latest }}" title: "chore(deps): bump Go from ${{ steps.check.outputs.current }} to ${{ steps.check.outputs.latest }}" body: | - Auto-generated PR to update Go version in go.mod. + Auto-generated PR to update Go version. - Current: `${{ steps.check.outputs.current }}` - Latest: `${{ steps.check.outputs.latest }}` + - Files: `go.mod`, `Dockerfile` base: develop \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 446d607..76234fb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,13 +19,13 @@ jobs: go-version: "1.26" - name: Build - run: make build + run: go build -ldflags "-s -w" -o bin/copia-cli ./cmd/copia-cli - name: Test run: go test -coverprofile=coverage.out ./... - name: Upload coverage to Codecov - uses: codecov/codecov-action@v5 + uses: codecov/codecov-action@v6 with: files: coverage.out token: ${{ secrets.CODECOV_TOKEN }} @@ -57,7 +57,7 @@ jobs: go-version: "1.26" - name: Integration tests - run: make integration + run: go test -tags=integration ./... env: COPIA_TEST_TOKEN: ${{ secrets.COPIA_TEST_TOKEN }} COPIA_TEST_HOST: ${{ secrets.COPIA_TEST_HOST }} diff --git a/.github/workflows/close-linked-issues.yml b/.github/workflows/close-linked-issues.yml index c8ab187..546ea66 100644 --- a/.github/workflows/close-linked-issues.yml +++ b/.github/workflows/close-linked-issues.yml @@ -15,7 +15,7 @@ jobs: pull-requests: read steps: - name: Close linked issues - uses: actions/github-script@v8 + uses: actions/github-script@v9 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | diff --git a/.github/workflows/copr.yml b/.github/workflows/copr.yml new file mode 100644 index 0000000..9ee457f --- /dev/null +++ b/.github/workflows/copr.yml @@ -0,0 +1,42 @@ +name: Publish to COPR + +on: + release: + types: [released] + +jobs: + copr: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Install copr-cli + run: | + sudo apt-get update + sudo apt-get install -y python3-pip rpm + pip3 install copr-cli + + - name: Configure copr-cli + run: | + mkdir -p ~/.config + printf '[copr-cli]\nlogin = %s\nusername = %s\ntoken = %s\ncopr_url = https://copr.fedorainfracloud.org\n' \ + "${{ secrets.COPR_LOGIN }}" "${{ secrets.COPR_USERNAME }}" "${{ secrets.COPR_TOKEN }}" \ + > ~/.config/copr + + - name: Extract version + id: version + run: echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" + + - name: Build SRPM + run: | + VERSION=${{ steps.version.outputs.version }} + curl -sL "https://github.com/${{ github.repository }}/archive/v${VERSION}/${GITHUB_REPOSITORY##*/}-${VERSION}.tar.gz" \ + -o rpmbuild/copia-cli-${VERSION}.tar.gz + rpmbuild -bs rpmbuild/copia-cli.spec \ + --define "version ${VERSION}" \ + --define "_sourcedir $(pwd)/rpmbuild" \ + --define "_srcrpmdir $(pwd)/rpmbuild" + + - name: Submit to COPR + run: | + copr-cli build copia-cli rpmbuild/copia-cli-*.src.rpm diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4eef3db..94ccafb 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -39,7 +39,7 @@ jobs: destination: ./_site - name: Upload Pages artifact - uses: actions/upload-pages-artifact@v4 + uses: actions/upload-pages-artifact@v5 deploy: needs: build diff --git a/.github/workflows/integration-scheduled.yml b/.github/workflows/integration-scheduled.yml new file mode 100644 index 0000000..dcefb61 --- /dev/null +++ b/.github/workflows/integration-scheduled.yml @@ -0,0 +1,41 @@ +name: Scheduled Integration Test + +on: + schedule: + - cron: "0 6 * * 1" # Weekly, Monday 6:00 UTC + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + integration: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: actions/setup-go@v6 + with: + go-version: "1.26" + + - name: Integration tests + run: go test -tags=integration -v ./... + env: + COPIA_TEST_TOKEN: ${{ secrets.COPIA_TEST_TOKEN }} + COPIA_TEST_HOST: ${{ secrets.COPIA_TEST_HOST }} + COPIA_TEST_OWNER: ${{ secrets.COPIA_TEST_OWNER }} + COPIA_TEST_REPO: ${{ secrets.COPIA_TEST_REPO }} + + - name: Create issue on failure + if: failure() + uses: actions/github-script@v9 + with: + script: | + const date = new Date().toISOString().split('T')[0]; + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + await github.rest.issues.create({ + ...context.repo, + title: `ci: scheduled integration test failure (${date})`, + body: `The scheduled integration test failed.\n\nWorkflow run: ${runUrl}\n\nThis may indicate a Copia API change.`, + labels: ['bug'] + }); diff --git a/.github/workflows/snap.yml b/.github/workflows/snap.yml new file mode 100644 index 0000000..6222313 --- /dev/null +++ b/.github/workflows/snap.yml @@ -0,0 +1,23 @@ +name: Publish to Snap Store + +on: + release: + types: [released] + +jobs: + snap: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - uses: snapcore/action-build@v1 + id: build + + - uses: snapcore/action-publish@v1 + env: + SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }} + with: + snap: ${{ steps.build.outputs.snap }} + release: stable diff --git a/.github/workflows/winget.yml b/.github/workflows/winget.yml new file mode 100644 index 0000000..822289e --- /dev/null +++ b/.github/workflows/winget.yml @@ -0,0 +1,16 @@ +name: Publish to WinGet + +on: + release: + types: [released] + +jobs: + publish: + runs-on: ubuntu-slim + steps: + - uses: vedantmgoyal9/winget-releaser@main + with: + identifier: Qubernetic.copia-cli + installers-regex: '\.zip$' + max-versions-to-keep: 5 + token: ${{ secrets.WINGET_TOKEN }} diff --git a/.gitignore b/.gitignore index 514f7ed..79d7993 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ thumbs.db # Secrets .env* +!.envrc.example # Test *.test diff --git a/.goreleaser.yml b/.goreleaser.yml index 0c2ff54..61123c5 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -77,7 +77,7 @@ nfpms: package_name: copia-cli vendor: Qubernetic homepage: "https://github.com/qubernetic/copia-cli" - maintainer: "Qubernetic " + maintainer: "Qubernetic " description: "CLI for Copia — source control for industrial automation" license: AGPL-3.0 formats: diff --git a/CHANGELOG.md b/CHANGELOG.md index 98b5f6c..ae45721 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,41 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.1.0] - 2026-04-16 + +### Added + +- WinGet automated publishing on release (#18, #190) +- COPR package for Fedora/RHEL — `dnf copr enable qubernetic/copia-cli` (#164, #193) +- AUR package for Arch Linux — `yay -S copia-cli-bin` (#161, #194) +- Snap package for Ubuntu/Linux — `snap install copia-cli --classic` (#162, #195) +- Scheduled weekly integration test with auto issue on failure (#134, #192) +- Tag protection ruleset for `v*` release tags (#149) + +### Changed + +- Dev environment migrated to Aurora-DX: Docker Compose + justfile, Makefile removed (#184, #185) +- Bump-go workflow now also updates Dockerfile (#188, #189) +- Installation docs updated with all new package managers (#191, #198) +- Maintainer email updated to info@qubernetic.com (#196, #197) + +### Fixed + +- AUR workflow: pkgbuild input expects file path, not inline content (#196) +- COPR workflow: heredoc indentation broke INI parser (#196) +- WinGet workflow: use ubuntu-slim per official docs (#196) +- Bump-go workflow: add setup-go step for go mod tidy (#196) + +### Dependencies + +- Go 1.26.1 → 1.26.2 (#186, #187) +- github.com/spf13/pflag 1.0.9 → 1.0.10 (#179) +- actions/github-script 8 → 9 (#183) +- actions/upload-pages-artifact 4 → 5 (#182) +- codecov/codecov-action 5 → 6 (#180) + +--- + ## [1.0.0] - 2026-04-03 ### Highlights diff --git a/CLAUDE.md b/CLAUDE.md index 9211ac8..440a01b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,17 +10,25 @@ Copia CLI is a command-line interface for [Copia](https://copia.io) — the sour ## Development Environment -Use the devcontainer for a fully configured environment. No local Go installation needed. +Aurora-DX workstation with Docker Compose + justfile. No local Go installation needed. ```bash -# VS Code / Cursor: "Reopen in Container" - -# CLI: -devcontainer up --workspace-folder . -devcontainer exec --workspace-folder . make build -devcontainer exec --workspace-folder . make test +# First-time setup +cp .envrc.example .envrc && direnv allow . +just setup + +# Daily workflow +just dev # Start the Go container +just test # Run tests (inside container) +just build # Build binary (inside container) +just lint # Run linter (inside container) +just down # Stop containers + +# VS Code / Cursor: "Reopen in Container" (uses docker-compose.dev.yaml) ``` +**Claude Code runs on the host** — never inside the devcontainer. Always launch from a host terminal. + ## Architecture Follows the `gh` CLI repository structure (`github.com/cli/cli`). @@ -39,7 +47,10 @@ copia-cli/ │ ├── api/ # Gitea SDK wrapper │ └── httpmock/ # HTTP mock for testing ├── docs/ # Developer documentation -└── Makefile +├── justfile # Command runner (replaces Makefile) +├── Dockerfile # Go dev image +├── docker-compose.yml # Base compose config +└── docker-compose.dev.yaml # Dev overlay (source mount + caches) ``` **Command structure:** `copia-cli [flags]` — mirrors `gh` CLI UX. @@ -65,7 +76,7 @@ copia-cli/ Phase 1 (MVP): auth, repo list/view/clone, issue CRUD, pr CRUD, label list/create — **DONE** Phase 2: release CRUD, repo create/delete/fork, pr review/diff/checkout, issue edit, Homebrew tap — **DONE** Phase 3: `copia-cli api` escape hatch, search, orgs, notifications, `-R` flag, completion, Jekyll manual, AGPL license — **DONE** -Phase 4: winget, OS keyring, aliases, browse, status dashboard, ssh-key, pr checks, changelog, collaborators +Phase 4: ~~winget~~, ~~COPR~~, ~~AUR~~, ~~Snap~~, OS keyring, aliases, browse, status dashboard, ssh-key, pr checks, changelog, collaborators **Out of scope:** workflow/run, codespace, copilot, project, cache, GUI diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c391533..154bae7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,36 +4,28 @@ Thank you for your interest in improving Copia CLI. ## Prerequisites -- **Docker** — required for devcontainer -- **VS Code / Cursor** — or the [devcontainer CLI](https://github.com/devcontainers/cli) +- **Docker** (or Podman) — required for the containerized dev environment +- **[just](https://just.systems/)** — command runner (`brew install just`) +- **[direnv](https://direnv.net/)** — per-directory env vars (`brew install direnv`) +- **VS Code / Cursor** — optional, supports "Reopen in Container" - **GitHub CLI (`gh`)** — recommended for issue/PR management ## Quick Start -The devcontainer provides a fully configured environment with Go 1.26+, gh CLI, and golangci-lint pre-installed. No local Go installation needed. - -**VS Code / Cursor:** - -1. Clone the repo and open it in your editor -2. "Reopen in Container" when prompted -3. All tools are pre-installed — start coding - -**CLI (without VS Code):** +All Go tooling runs inside a Docker container. No local Go installation needed. ```bash git clone https://github.com/qubernetic/copia-cli.git cd copia-cli -# Build and start the devcontainer -npx @devcontainers/cli up --workspace-folder . - -# Run commands inside the container -npx @devcontainers/cli exec --workspace-folder . make build -npx @devcontainers/cli exec --workspace-folder . make test -npx @devcontainers/cli exec --workspace-folder . ./bin/copia-cli --version +cp .envrc.example .envrc && direnv allow . +just setup # Build image + download Go deps +just dev # Start the dev container +just test # Run tests +just build # Build binary ``` -> **Note:** `npx` runs the devcontainer CLI without global installation. Install globally with `npm install -g @devcontainers/cli` to use `devcontainer` directly. +**VS Code / Cursor:** "Reopen in Container" uses the same Docker Compose stack. ## Development Workflow @@ -48,7 +40,7 @@ This repo follows a strict Gitflow workflow. Every contribution goes through the git checkout -b /- 4. Make atomic commits using Conventional Commits format 5. Run tests: - make test + just test 6. Push and open a PR targeting develop: git push -u origin /- gh pr create --base develop @@ -88,16 +80,14 @@ Use imperative mood, lowercase after colon, no period. Before submitting a PR: -1. **Unit tests:** `make test` -2. **Build:** `make build` -3. **Lint:** `golangci-lint run ./...` (if installed) +1. **Unit tests:** `just test` +2. **Build:** `just build` +3. **Lint:** `just lint` -Integration tests require a Copia API token and run separately: +Integration tests require a Copia API token (set in `.envrc`) and run separately: ```bash -export COPIA_TEST_TOKEN=your-token -export COPIA_TEST_HOST=app.copia.io -make integration +just integration ``` ## Project Structure diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..bb1ad0a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM golang:1.26.2-alpine + +RUN apk add --no-cache git build-base + +RUN git config --global --add safe.directory /app + +WORKDIR /app + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . diff --git a/Makefile b/Makefile deleted file mode 100644 index 6dccc81..0000000 --- a/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -BIN := copia-cli -VERSION ?= DEV -DATE := $(shell date -u +%Y-%m-%d) -LDFLAGS := -s -w \ - -X github.com/qubernetic/copia-cli/internal/build.Version=$(VERSION) \ - -X github.com/qubernetic/copia-cli/internal/build.Date=$(DATE) - -.PHONY: build test integration acceptance docs docs-serve docs-clean clean snapshot install uninstall update - -build: - go build -ldflags "$(LDFLAGS)" -o bin/$(BIN) ./cmd/copia-cli - -test: - go test ./... - -integration: - go test -tags=integration ./... - -acceptance: - go test -tags=acceptance ./acceptance/... - -docs: - go run script/gen-docs.go - -docs-serve: docs - cd docs/site && bundle exec jekyll serve --baseurl /copia-cli - -docs-clean: - rm -rf docs/site/_site docs/site/.jekyll-cache docs/site/manual/copia-cli_*.md docs/site/_includes/sidebar.html - -clean: - rm -rf bin/ dist/ - -snapshot: - goreleaser release --snapshot --clean - -install: ## Install rpm (Fedora/RHEL only) - sudo dnf install -y dist/$(BIN)_*_linux_amd64.rpm - -uninstall: - sudo dnf remove -y $(BIN) - -update: snapshot - sudo dnf remove -y $(BIN) || true - sudo dnf install -y dist/$(BIN)_*_linux_amd64.rpm diff --git a/README.md b/README.md index e7c836a..6bc3a37 100644 --- a/README.md +++ b/README.md @@ -33,12 +33,15 @@ If anything feels off, or if you feel that some functionality is missing, please ### [Linux](docs/install_linux.md) - [Homebrew](docs/install_linux.md#homebrew) +- [Fedora/RHEL (COPR)](docs/install_linux.md#fedorarhel-copr) - [Debian/Ubuntu (.deb)](docs/install_linux.md#debianubuntu-deb) -- [Fedora/RHEL (.rpm)](docs/install_linux.md#fedorarhel-rpm) +- [Snap](docs/install_linux.md#snap) +- [AUR (Arch Linux)](docs/install_linux.md#aur-arch-linux) - [Precompiled binaries](docs/install_linux.md#precompiled-binaries) on [releases page][] ### [Windows](docs/install_windows.md) +- [WinGet](docs/install_windows.md#winget) - [Precompiled binaries](docs/install_windows.md#precompiled-binaries) on [releases page][] ### Build from source @@ -73,7 +76,7 @@ Shell completion is available for Bash, Zsh, Fish, and PowerShell. See `copia-cl - **Phase 1 (MVP):** auth, repo list/view/clone, issue CRUD, pr CRUD, label list/create — **Done** - **Phase 2 (Workflow):** release CRUD, repo create/delete/fork, pr review/diff/checkout, issue edit, Homebrew tap — **Done** - **Phase 3 (Power Features):** `api` escape hatch, search, orgs, notifications, `-R`/`--repo` flag, completion, Jekyll manual, AGPL license — **Done** -- **Phase 4 (Nice to Have):** winget, OS keyring, aliases, browse, status dashboard, ssh-key, pr checks, scheduled integration tests +- **Phase 4 (Nice to Have):** ~~winget~~, ~~COPR~~, ~~AUR~~, ~~Snap~~, OS keyring, aliases, browse, status dashboard, ssh-key, pr checks, scheduled integration tests ## License diff --git a/docker-compose.dev.yaml b/docker-compose.dev.yaml new file mode 100644 index 0000000..07baf00 --- /dev/null +++ b/docker-compose.dev.yaml @@ -0,0 +1,14 @@ +services: + go: + build: + context: . + dockerfile: Dockerfile + volumes: + - .:/app + - go-mod-cache:/go/pkg/mod + - go-build-cache:/root/.cache/go-build + command: ["sleep", "infinity"] + +volumes: + go-mod-cache: + go-build-cache: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..81d8f58 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,5 @@ +services: + go: + build: . + working_dir: /app + command: ["sleep", "infinity"] diff --git a/docs/install_linux.md b/docs/install_linux.md index 52e1c38..1368924 100644 --- a/docs/install_linux.md +++ b/docs/install_linux.md @@ -26,18 +26,40 @@ curl -LO https://github.com/qubernetic/copia-cli/releases/latest/download/copia- sudo dpkg -i copia-cli_*_linux_amd64.deb ``` -### Fedora/RHEL (.rpm) +### Fedora/RHEL (COPR) -Download the `.rpm` package from [GitHub Releases](https://github.com/qubernetic/copia-cli/releases/latest): +```bash +sudo dnf copr enable qubernetic/copia-cli +sudo dnf install copia-cli +``` + +To upgrade: ```bash -# Download the latest .rpm (amd64) -curl -LO https://github.com/qubernetic/copia-cli/releases/latest/download/copia-cli_*_linux_amd64.rpm +sudo dnf upgrade copia-cli +``` -# Install +Alternatively, download the `.rpm` package directly from [GitHub Releases](https://github.com/qubernetic/copia-cli/releases/latest): + +```bash +curl -LO https://github.com/qubernetic/copia-cli/releases/latest/download/copia-cli_*_linux_amd64.rpm sudo dnf install -y copia-cli_*_linux_amd64.rpm ``` +### Snap + +```bash +sudo snap install copia-cli --classic +``` + +### AUR (Arch Linux) + +```bash +yay -S copia-cli-bin +``` + +Or with any other AUR helper. The package is [`copia-cli-bin`](https://aur.archlinux.org/packages/copia-cli-bin). + ### Precompiled binaries [Copia CLI releases](https://github.com/qubernetic/copia-cli/releases/latest) contain precompiled binaries for `amd64`, `arm64`, `386`, and `armv6` architectures. diff --git a/docs/install_source.md b/docs/install_source.md index e656141..b0a6db7 100644 --- a/docs/install_source.md +++ b/docs/install_source.md @@ -1,12 +1,6 @@ # Installation from source -1. Verify that you have Go 1.26+ installed - - ```bash - $ go version - ``` - - If `go` is not installed, follow instructions on [the Go website](https://golang.org/doc/install). +1. Install [Docker](https://docs.docker.com/get-docker/) and [just](https://just.systems/) 2. Clone this repository @@ -15,10 +9,12 @@ $ cd copia-cli ``` -3. Build and install +3. Build inside the container ```bash - $ make build + $ just setup + $ just dev + $ just build $ sudo cp bin/copia-cli /usr/local/bin/ ``` @@ -28,17 +24,8 @@ $ copia-cli --version ``` -## Cross-compilation - -The Makefile supports cross-compilation via Go environment variables: +Alternatively, if you have Go 1.26+ installed locally: ```bash -# Linux ARM64 -$ GOOS=linux GOARCH=arm64 make build - -# macOS ARM64 -$ GOOS=darwin GOARCH=arm64 make build - -# Windows AMD64 -$ GOOS=windows GOARCH=amd64 make build +$ go build -o bin/copia-cli ./cmd/copia-cli ``` diff --git a/docs/install_windows.md b/docs/install_windows.md index 14f4dae..d5ed77d 100644 --- a/docs/install_windows.md +++ b/docs/install_windows.md @@ -2,6 +2,12 @@ ## Recommended _(Official)_ +### WinGet + +```pwsh +winget install Qubernetic.copia-cli +``` + ### Precompiled binaries [Copia CLI releases](https://github.com/qubernetic/copia-cli/releases/latest) contain precompiled `.zip` archives for `amd64`, `arm64`, and `386` architectures. @@ -13,17 +19,6 @@ > [!NOTE] > When using Windows Terminal, you will need to **open a new window** for PATH changes to take effect. -## Community _(Unofficial)_ - -### Scoop - -```pwsh -scoop install copia-cli -``` - -> [!NOTE] -> Scoop support is community-maintained and may lag behind official releases. - ## Building from source See [install_source.md](install_source.md). diff --git a/docs/project-layout.md b/docs/project-layout.md index 9fa6a64..fe467b3 100644 --- a/docs/project-layout.md +++ b/docs/project-layout.md @@ -79,9 +79,12 @@ copia-cli/ │ └── httpmock/registry.go # HTTP transport mock for testing ├── test/integration/ # Live API integration tests ├── .github/workflows/ # CI, CodeQL, release, govulncheck, bump-go -├── .devcontainer/ # Go 1.26.1, gh CLI, Claude Code +├── .devcontainer/ # Docker Compose-based dev environment ├── .goreleaser.yml # Cross-platform release config -└── Makefile # build, test, integration, clean +├── justfile # Command runner (build, test, lint, dev) +├── Dockerfile # Go 1.26.2 dev image +├── docker-compose.yml # Base compose config +└── docker-compose.dev.yaml # Dev overlay (source mount + caches) ``` ## Module Guide diff --git a/docs/releasing.md b/docs/releasing.md index a49a424..a236efc 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -35,7 +35,12 @@ - Uploads all artifacts to a GitHub Release (draft) - Updates the [Homebrew formula](https://github.com/qubernetic/homebrew-tap) -7. Review and publish the draft release on GitHub +7. Review and publish the draft release on GitHub. Publishing triggers: + + - [WinGet](.github/workflows/winget.yml) — updates `Qubernetic.copia-cli` in winget-pkgs + - [COPR](.github/workflows/copr.yml) — builds and publishes RPM to Fedora COPR + - [AUR](.github/workflows/aur.yml) — updates `copia-cli-bin` on AUR + - [Snap](.github/workflows/snap.yml) — builds and publishes to the Snap Store 8. Back-merge `main` into `develop`: @@ -52,7 +57,7 @@ The release build is powered by [GoReleaser](https://goreleaser.com/). Configura To test the release locally without publishing: ```bash -$ make snapshot +$ just snapshot ``` ## Pre-releases diff --git a/go.mod b/go.mod index ded0184..2ef8880 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,11 @@ module github.com/qubernetic/copia-cli -go 1.26.1 +go 1.26.2 require ( code.gitea.io/sdk/gitea v0.24.1 github.com/spf13/cobra v1.10.2 - github.com/spf13/pflag v1.0.9 + github.com/spf13/pflag v1.0.10 github.com/stretchr/testify v1.11.1 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index f9fcdc3..6f1899f 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,9 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= -github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= diff --git a/justfile b/justfile new file mode 100644 index 0000000..0e85f38 --- /dev/null +++ b/justfile @@ -0,0 +1,87 @@ +set shell := ["bash", "-euo", "pipefail", "-c"] + +bin := "copia-cli" +version := env("VERSION", "DEV") +date := `date -u +%Y-%m-%d` +ldflags := "-s -w -X github.com/qubernetic/copia-cli/internal/build.Version=" + version + " -X github.com/qubernetic/copia-cli/internal/build.Date=" + date +compose_dev := "docker compose -f docker-compose.yml -f docker-compose.dev.yaml" + +# List all recipes +default: + @just --list --unsorted + +# ─── Lifecycle ──────── + +# First-time setup: build image + download deps +setup: + {{ compose_dev }} build + {{ compose_dev }} run --rm go go mod download + +# Start dev container (detached) +dev: + {{ compose_dev }} up -d + +# Stop containers (keep volumes) +down: + {{ compose_dev }} down + +# Nuclear: remove containers + volumes +clean: + {{ compose_dev }} down -v + rm -rf bin/ dist/ + +# Rebuild image after Dockerfile changes +rebuild: + {{ compose_dev }} build --no-cache + +# Shell into the go container +shell: + {{ compose_dev }} exec go sh + +# ─── Build ──────────── + +# Build the CLI binary +build: + {{ compose_dev }} exec go go build -ldflags "{{ ldflags }}" -o bin/{{ bin }} ./cmd/copia-cli + +# ─── Checks ─────────── + +# Run unit tests +test: + {{ compose_dev }} exec go go test ./... + +# Run integration tests (requires COPIA_TOKEN) +integration: + {{ compose_dev }} exec go go test -tags=integration ./... + +# Run acceptance tests +acceptance: + {{ compose_dev }} exec go go test -tags=acceptance ./acceptance/... + +# Run linter +lint: + {{ compose_dev }} exec go golangci-lint run ./... + +# Format code +fmt: + {{ compose_dev }} exec go gofmt -w . + +# ─── Docs ───────────── + +# Generate CLI manual pages +docs: + {{ compose_dev }} exec go go run script/gen-docs.go + +# Serve docs site locally +docs-serve: docs + cd docs/site && bundle exec jekyll serve --baseurl /copia-cli + +# Clean docs build artifacts +docs-clean: + rm -rf docs/site/_site docs/site/.jekyll-cache docs/site/manual/copia-cli_*.md docs/site/_includes/sidebar.html + +# ─── Release ────────── + +# Create a local snapshot release +snapshot: + {{ compose_dev }} exec go goreleaser release --snapshot --clean diff --git a/rpmbuild/copia-cli.spec b/rpmbuild/copia-cli.spec new file mode 100644 index 0000000..cccf91a --- /dev/null +++ b/rpmbuild/copia-cli.spec @@ -0,0 +1,33 @@ +%global goipath github.com/qubernetic/copia-cli + +Name: copia-cli +Version: %{version} +Release: 1%{?dist} +Summary: CLI for Copia — source control for industrial automation + +License: AGPL-3.0-only +URL: https://%{goipath} +Source0: https://%{goipath}/archive/v%{version}/%{name}-%{version}.tar.gz + +BuildRequires: golang >= 1.26 + +%description +copia-cli brings Copia repositories, issues, pull requests, and other +concepts to the terminal next to where you are already working with +git and your code. + +%prep +%autosetup -n %{name}-%{version} + +%build +go build -ldflags "-s -w -X %{goipath}/internal/build.Version=%{version} -X %{goipath}/internal/build.Date=$(date -u +%%Y-%%m-%%d)" -o %{name} ./cmd/copia-cli + +%install +install -Dpm 0755 %{name} %{buildroot}%{_bindir}/%{name} +install -Dpm 0644 LICENSE %{buildroot}%{_licensedir}/%{name}/LICENSE + +%files +%license LICENSE +%{_bindir}/%{name} + +%changelog diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml new file mode 100644 index 0000000..5d4886a --- /dev/null +++ b/snap/snapcraft.yaml @@ -0,0 +1,32 @@ +name: copia-cli +version: git +summary: CLI for Copia — source control for industrial automation +description: | + copia-cli brings Copia repositories, issues, pull requests, and other + concepts to the terminal next to where you are already working with + git and your code. + +base: core24 +grade: stable +confinement: classic +license: AGPL-3.0 + +platforms: + amd64: + arm64: + +parts: + copia-cli: + plugin: go + source: . + source-type: local + build-snaps: + - go/1.26/stable + go-buildtags: [] + go-generate: [] + override-build: | + go build -ldflags "-s -w -X github.com/qubernetic/copia-cli/internal/build.Version=$(git describe --tags --always) -X github.com/qubernetic/copia-cli/internal/build.Date=$(date -u +%Y-%m-%d)" -o $CRAFT_PART_INSTALL/bin/copia-cli ./cmd/copia-cli + +apps: + copia-cli: + command: bin/copia-cli