feat/ml-integration (#2)
Co-authored-by: Charon (snider-linux) <charon@lethean.io> Co-authored-by: Snider <snider@host.uk.com> Co-authored-by: Virgil <virgil@lethean.io> Co-authored-by: Claude <developers@lethean.io> Reviewed-on: #2 Co-authored-by: Snider <snider@lethean.io> Co-committed-by: Snider <snider@lethean.io>
This commit is contained in:
parent
50da0adcb7
commit
a1306bc321
76 changed files with 1375 additions and 4500 deletions
|
|
@ -30,7 +30,7 @@ core/
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/host-uk/core/pkg/cli"
|
"forge.lthn.ai/core/cli/pkg/cli"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ func NewNameCmd() *cobra.Command {
|
||||||
## CLI Output Helpers
|
## CLI Output Helpers
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "github.com/host-uk/core/pkg/cli"
|
import "forge.lthn.ai/core/cli/pkg/cli"
|
||||||
|
|
||||||
cli.Success("Operation completed") // Green check
|
cli.Success("Operation completed") // Green check
|
||||||
cli.Warning("Something to note") // Yellow warning
|
cli.Warning("Something to note") // Yellow warning
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# Issue 258: Smart Test Detection
|
# Issue 258: Smart Test Detection
|
||||||
|
|
||||||
## Original Issue
|
## Original Issue
|
||||||
<https://github.com/host-uk/core/issues/258>
|
<https://forge.lthn.ai/core/cli/issues/258>
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
Make `core test` smart — detect changed Go files and run only relevant tests.
|
Make `core test` smart — detect changed Go files and run only relevant tests.
|
||||||
|
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
name: Bug Report
|
|
||||||
description: Report a problem with the core CLI
|
|
||||||
title: "[Bug]: "
|
|
||||||
labels: ["bug", "triage"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for reporting! Please fill out the details below.
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: os
|
|
||||||
attributes:
|
|
||||||
label: Operating System
|
|
||||||
options:
|
|
||||||
- macOS
|
|
||||||
- Windows
|
|
||||||
- Linux (Ubuntu/Debian)
|
|
||||||
- Linux (Other)
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: command
|
|
||||||
attributes:
|
|
||||||
label: Command
|
|
||||||
description: Which command failed?
|
|
||||||
placeholder: "e.g., core dev work, core php test"
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: input
|
|
||||||
id: version
|
|
||||||
attributes:
|
|
||||||
label: Version
|
|
||||||
description: Output of `core version`
|
|
||||||
placeholder: "e.g., core v0.1.0"
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: description
|
|
||||||
attributes:
|
|
||||||
label: What happened?
|
|
||||||
description: Describe the issue
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: expected
|
|
||||||
attributes:
|
|
||||||
label: Expected behaviour
|
|
||||||
description: What should have happened?
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: logs
|
|
||||||
attributes:
|
|
||||||
label: Error output
|
|
||||||
description: Paste any error messages
|
|
||||||
render: shell
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
blank_issues_enabled: true
|
|
||||||
contact_links:
|
|
||||||
- name: Host UK Documentation
|
|
||||||
url: https://github.com/host-uk/core-devops
|
|
||||||
about: Setup guides and workspace documentation
|
|
||||||
- name: Discussions
|
|
||||||
url: https://github.com/orgs/host-uk/discussions
|
|
||||||
about: Ask questions and share ideas
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
||||||
name: Feature Request
|
|
||||||
description: Suggest a new feature or enhancement
|
|
||||||
title: "[Feature]: "
|
|
||||||
labels: ["enhancement"]
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: |
|
|
||||||
Thanks for the suggestion! Please describe your idea below.
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: area
|
|
||||||
attributes:
|
|
||||||
label: Area
|
|
||||||
options:
|
|
||||||
- dev commands (work, commit, push, pull)
|
|
||||||
- php commands (test, lint, stan)
|
|
||||||
- GitHub integration (issues, reviews, ci)
|
|
||||||
- New command
|
|
||||||
- Documentation
|
|
||||||
- Other
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: problem
|
|
||||||
attributes:
|
|
||||||
label: Problem or use case
|
|
||||||
description: What problem does this solve?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: solution
|
|
||||||
attributes:
|
|
||||||
label: Proposed solution
|
|
||||||
description: How would you like it to work?
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
- type: textarea
|
|
||||||
id: alternatives
|
|
||||||
attributes:
|
|
||||||
label: Alternatives considered
|
|
||||||
description: Any other approaches you've thought about?
|
|
||||||
|
|
||||||
- type: dropdown
|
|
||||||
id: complexity
|
|
||||||
attributes:
|
|
||||||
label: Estimated complexity
|
|
||||||
description: How much work do you think this requires?
|
|
||||||
options:
|
|
||||||
- "Small - Quick fix, single file, < 1 hour"
|
|
||||||
- "Medium - Multiple files, few hours to a day"
|
|
||||||
- "Large - Significant changes, multiple days"
|
|
||||||
- "Unknown - Not sure"
|
|
||||||
validations:
|
|
||||||
required: false
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "gomod"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
day: "monday"
|
|
||||||
labels:
|
|
||||||
- "type:dependencies"
|
|
||||||
- "priority:low"
|
|
||||||
commit-message:
|
|
||||||
prefix: "deps(go):"
|
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "weekly"
|
|
||||||
day: "monday"
|
|
||||||
labels:
|
|
||||||
- "type:dependencies"
|
|
||||||
- "priority:low"
|
|
||||||
commit-message:
|
|
||||||
prefix: "deps(actions):"
|
|
||||||
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
name: Agent Verification
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [labeled]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
verify:
|
|
||||||
uses: host-uk/.github/.github/workflows/agent-verify.yml@main
|
|
||||||
secrets: inherit
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
|
|
||||||
name: "Alpha Release: Manual"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
id-token: write
|
|
||||||
attestations: write
|
|
||||||
|
|
||||||
env:
|
|
||||||
NEXT_VERSION: "0.0.4"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
platform: linux/amd64
|
|
||||||
- os: ubuntu-latest
|
|
||||||
platform: linux/arm64
|
|
||||||
- os: macos-latest
|
|
||||||
platform: darwin/universal
|
|
||||||
- os: windows-latest
|
|
||||||
platform: windows/amd64
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
uses: host-uk/build@v3
|
|
||||||
with:
|
|
||||||
build-name: core
|
|
||||||
build-platform: ${{ matrix.platform }}
|
|
||||||
build: true
|
|
||||||
package: true
|
|
||||||
sign: false
|
|
||||||
|
|
||||||
release:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Prepare release files
|
|
||||||
run: |
|
|
||||||
mkdir -p release
|
|
||||||
cp dist/* release/ 2>/dev/null || true
|
|
||||||
ls -la release/
|
|
||||||
|
|
||||||
- name: Create alpha release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
VERSION="v${{ env.NEXT_VERSION }}-alpha.${{ github.run_number }}"
|
|
||||||
|
|
||||||
gh release create "$VERSION" \
|
|
||||||
--title "Alpha: $VERSION" \
|
|
||||||
--notes "Canary build from dev branch.
|
|
||||||
|
|
||||||
**Version:** $VERSION
|
|
||||||
**Commit:** ${{ github.sha }}
|
|
||||||
**Built:** $(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
|
||||||
**Run:** ${{ github.run_id }}
|
|
||||||
|
|
||||||
## Channel: Alpha (Canary)
|
|
||||||
|
|
||||||
This is an automated pre-release for early testing.
|
|
||||||
|
|
||||||
- Systems and early adopters can test breaking changes
|
|
||||||
- Quality scoring determines promotion to beta
|
|
||||||
- Use stable releases for production
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# macOS/Linux
|
|
||||||
curl -fsSL https://github.com/host-uk/core/releases/download/$VERSION/core-linux-amd64 -o core
|
|
||||||
chmod +x core && sudo mv core /usr/local/bin/
|
|
||||||
\`\`\`
|
|
||||||
" \
|
|
||||||
--prerelease \
|
|
||||||
--target dev \
|
|
||||||
release/*
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push
|
|
||||||
name: "Alpha Release: Push"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
id-token: write
|
|
||||||
attestations: write
|
|
||||||
|
|
||||||
env:
|
|
||||||
NEXT_VERSION: "0.0.4"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
platform: linux/amd64
|
|
||||||
- os: ubuntu-latest
|
|
||||||
platform: linux/arm64
|
|
||||||
- os: macos-latest
|
|
||||||
platform: darwin/universal
|
|
||||||
- os: windows-latest
|
|
||||||
platform: windows/amd64
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
uses: host-uk/build@v3
|
|
||||||
with:
|
|
||||||
build-name: core
|
|
||||||
build-platform: ${{ matrix.platform }}
|
|
||||||
build: true
|
|
||||||
package: true
|
|
||||||
sign: false
|
|
||||||
|
|
||||||
release:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Prepare release files
|
|
||||||
run: |
|
|
||||||
mkdir -p release
|
|
||||||
cp dist/* release/ 2>/dev/null || true
|
|
||||||
ls -la release/
|
|
||||||
|
|
||||||
- name: Create alpha release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
VERSION="v${{ env.NEXT_VERSION }}-alpha.${{ github.run_number }}"
|
|
||||||
|
|
||||||
gh release create "$VERSION" \
|
|
||||||
--title "Alpha: $VERSION" \
|
|
||||||
--notes "Canary build from dev branch.
|
|
||||||
|
|
||||||
**Version:** $VERSION
|
|
||||||
**Commit:** ${{ github.sha }}
|
|
||||||
**Built:** $(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
|
||||||
**Run:** ${{ github.run_id }}
|
|
||||||
|
|
||||||
## Channel: Alpha (Canary)
|
|
||||||
|
|
||||||
This is an automated pre-release for early testing.
|
|
||||||
|
|
||||||
- Systems and early adopters can test breaking changes
|
|
||||||
- Quality scoring determines promotion to beta
|
|
||||||
- Use stable releases for production
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# macOS/Linux
|
|
||||||
curl -fsSL https://github.com/host-uk/core/releases/download/$VERSION/core-linux-amd64 -o core
|
|
||||||
chmod +x core && sudo mv core /usr/local/bin/
|
|
||||||
\`\`\`
|
|
||||||
" \
|
|
||||||
--prerelease \
|
|
||||||
--target dev \
|
|
||||||
release/*
|
|
||||||
|
|
@ -1,500 +0,0 @@
|
||||||
name: Alpha Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
id-token: write
|
|
||||||
attestations: write
|
|
||||||
|
|
||||||
env:
|
|
||||||
# Next version - update when releasing
|
|
||||||
NEXT_VERSION: "0.0.4"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
- os: ubuntu-latest
|
|
||||||
goos: linux
|
|
||||||
goarch: arm64
|
|
||||||
- os: macos-latest
|
|
||||||
goos: darwin
|
|
||||||
goarch: arm64
|
|
||||||
- os: windows-latest
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
# GUI build disabled until build action supports Wails v3
|
|
||||||
# - name: Wails Build Action
|
|
||||||
# uses: host-uk/build@v4.0.0
|
|
||||||
# with:
|
|
||||||
# build-name: core
|
|
||||||
# build-platform: ${{ matrix.goos }}/${{ matrix.goarch }}
|
|
||||||
# build: true
|
|
||||||
# package: true
|
|
||||||
# sign: false
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: host-uk/build/actions/setup/go@v4.0.0
|
|
||||||
with:
|
|
||||||
go-version: "1.25"
|
|
||||||
|
|
||||||
- name: Build CLI
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
EXT=""
|
|
||||||
if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi
|
|
||||||
BINARY="core${EXT}"
|
|
||||||
ARCHIVE_PREFIX="core-${GOOS}-${GOARCH}"
|
|
||||||
|
|
||||||
APP_VERSION="${{ env.NEXT_VERSION }}-alpha.${{ github.run_number }}"
|
|
||||||
go build -ldflags "-s -w -X github.com/host-uk/core/pkg/cli.AppVersion=${APP_VERSION}" -o "./bin/${BINARY}" .
|
|
||||||
|
|
||||||
# Create tar.gz for Homebrew (non-Windows)
|
|
||||||
if [ "$GOOS" != "windows" ]; then
|
|
||||||
tar czf "./bin/${ARCHIVE_PREFIX}.tar.gz" -C ./bin "${BINARY}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create zip for Scoop (Windows)
|
|
||||||
if [ "$GOOS" = "windows" ]; then
|
|
||||||
cd ./bin && zip "${ARCHIVE_PREFIX}.zip" "${BINARY}" && cd ..
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rename raw binary to platform-specific name for release
|
|
||||||
mv "./bin/${BINARY}" "./bin/${ARCHIVE_PREFIX}${EXT}"
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: core-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
||||||
path: ./bin/core-*
|
|
||||||
|
|
||||||
build-ide:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: macos-latest
|
|
||||||
goos: darwin
|
|
||||||
goarch: arm64
|
|
||||||
- os: ubuntu-latest
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
- os: windows-latest
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: internal/core-ide
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: host-uk/build/actions/setup/go@v4.0.0
|
|
||||||
with:
|
|
||||||
go-version: "1.25"
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "20"
|
|
||||||
|
|
||||||
- name: Install Wails CLI
|
|
||||||
run: go install github.com/wailsapp/wails/v3/cmd/wails3@latest
|
|
||||||
|
|
||||||
- name: Install frontend dependencies
|
|
||||||
working-directory: internal/core-ide/frontend
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Generate bindings
|
|
||||||
run: wails3 generate bindings -f '-tags production' -clean=false -ts -i
|
|
||||||
|
|
||||||
- name: Build frontend
|
|
||||||
working-directory: internal/core-ide/frontend
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Install Linux dependencies
|
|
||||||
if: matrix.goos == 'linux'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev
|
|
||||||
|
|
||||||
- name: Build IDE
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
EXT=""
|
|
||||||
if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi
|
|
||||||
BINARY="core-ide${EXT}"
|
|
||||||
ARCHIVE_PREFIX="core-ide-${GOOS}-${GOARCH}"
|
|
||||||
|
|
||||||
BUILD_FLAGS="-tags production -trimpath -buildvcs=false"
|
|
||||||
|
|
||||||
if [ "$GOOS" = "windows" ]; then
|
|
||||||
# Windows: no CGO, use windowsgui linker flag
|
|
||||||
export CGO_ENABLED=0
|
|
||||||
LDFLAGS="-w -s -H windowsgui"
|
|
||||||
|
|
||||||
# Generate Windows syso resource
|
|
||||||
cd build
|
|
||||||
wails3 generate syso -arch ${GOARCH} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_${GOARCH}.syso
|
|
||||||
cd ..
|
|
||||||
elif [ "$GOOS" = "darwin" ]; then
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
export CGO_CFLAGS="-mmacosx-version-min=10.15"
|
|
||||||
export CGO_LDFLAGS="-mmacosx-version-min=10.15"
|
|
||||||
export MACOSX_DEPLOYMENT_TARGET="10.15"
|
|
||||||
LDFLAGS="-w -s"
|
|
||||||
else
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
LDFLAGS="-w -s"
|
|
||||||
fi
|
|
||||||
|
|
||||||
go build ${BUILD_FLAGS} -ldflags="${LDFLAGS}" -o "./bin/${BINARY}"
|
|
||||||
|
|
||||||
# Clean up syso files
|
|
||||||
rm -f *.syso
|
|
||||||
|
|
||||||
# Package
|
|
||||||
if [ "$GOOS" = "darwin" ]; then
|
|
||||||
# Create .app bundle
|
|
||||||
mkdir -p "./bin/Core IDE.app/Contents/"{MacOS,Resources}
|
|
||||||
cp build/darwin/icons.icns "./bin/Core IDE.app/Contents/Resources/"
|
|
||||||
cp "./bin/${BINARY}" "./bin/Core IDE.app/Contents/MacOS/"
|
|
||||||
cp build/darwin/Info.plist "./bin/Core IDE.app/Contents/"
|
|
||||||
codesign --force --deep --sign - "./bin/Core IDE.app"
|
|
||||||
tar czf "./bin/${ARCHIVE_PREFIX}.tar.gz" -C ./bin "Core IDE.app"
|
|
||||||
elif [ "$GOOS" = "windows" ]; then
|
|
||||||
cd ./bin && zip "${ARCHIVE_PREFIX}.zip" "${BINARY}" && cd ..
|
|
||||||
else
|
|
||||||
tar czf "./bin/${ARCHIVE_PREFIX}.tar.gz" -C ./bin "${BINARY}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rename raw binary
|
|
||||||
mv "./bin/${BINARY}" "./bin/${ARCHIVE_PREFIX}${EXT}"
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: core-ide-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
||||||
path: internal/core-ide/bin/core-ide-*
|
|
||||||
|
|
||||||
release:
|
|
||||||
needs: [build, build-ide]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
version: ${{ steps.version.outputs.version }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set version
|
|
||||||
id: version
|
|
||||||
run: echo "version=v${{ env.NEXT_VERSION }}-alpha.${{ github.run_number }}" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Prepare release files
|
|
||||||
run: |
|
|
||||||
mkdir -p release
|
|
||||||
cp dist/* release/ 2>/dev/null || true
|
|
||||||
ls -la release/
|
|
||||||
|
|
||||||
- name: Create alpha release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
VERSION: ${{ steps.version.outputs.version }}
|
|
||||||
run: |
|
|
||||||
gh release create "$VERSION" \
|
|
||||||
--title "Alpha: $VERSION" \
|
|
||||||
--notes "Canary build from dev branch.
|
|
||||||
|
|
||||||
**Version:** $VERSION
|
|
||||||
**Commit:** ${{ github.sha }}
|
|
||||||
**Built:** $(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
|
||||||
**Run:** ${{ github.run_id }}
|
|
||||||
|
|
||||||
## Channel: Alpha (Canary)
|
|
||||||
|
|
||||||
This is an automated pre-release for early testing.
|
|
||||||
|
|
||||||
- Systems and early adopters can test breaking changes
|
|
||||||
- Quality scoring determines promotion to beta
|
|
||||||
- Use stable releases for production
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
\`\`\`bash
|
|
||||||
# Homebrew (macOS/Linux)
|
|
||||||
brew install host-uk/tap/core
|
|
||||||
|
|
||||||
# Scoop (Windows)
|
|
||||||
scoop bucket add host-uk https://github.com/host-uk/scoop-bucket
|
|
||||||
scoop install core
|
|
||||||
|
|
||||||
# Direct download (example: Linux amd64)
|
|
||||||
curl -fsSL https://github.com/host-uk/core/releases/download/$VERSION/core-linux-amd64 -o core
|
|
||||||
chmod +x core && sudo mv core /usr/local/bin/
|
|
||||||
\`\`\`
|
|
||||||
" \
|
|
||||||
--prerelease \
|
|
||||||
--target dev \
|
|
||||||
release/*
|
|
||||||
|
|
||||||
update-tap:
|
|
||||||
needs: release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Generate checksums
|
|
||||||
run: |
|
|
||||||
cd dist
|
|
||||||
for f in *.tar.gz; do
|
|
||||||
sha256sum "$f" | awk '{print $1}' > "${f}.sha256"
|
|
||||||
done
|
|
||||||
echo "=== Checksums ==="
|
|
||||||
cat *.sha256
|
|
||||||
|
|
||||||
- name: Update Homebrew formula
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
|
||||||
VERSION: ${{ needs.release.outputs.version }}
|
|
||||||
run: |
|
|
||||||
# Strip leading 'v' for formula version
|
|
||||||
FORMULA_VERSION="${VERSION#v}"
|
|
||||||
|
|
||||||
# Read checksums
|
|
||||||
DARWIN_ARM64=$(cat dist/core-darwin-arm64.tar.gz.sha256)
|
|
||||||
LINUX_AMD64=$(cat dist/core-linux-amd64.tar.gz.sha256)
|
|
||||||
LINUX_ARM64=$(cat dist/core-linux-arm64.tar.gz.sha256)
|
|
||||||
|
|
||||||
# Clone tap repo (configure auth for push)
|
|
||||||
gh repo clone host-uk/homebrew-tap /tmp/tap -- --depth=1
|
|
||||||
cd /tmp/tap
|
|
||||||
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/host-uk/homebrew-tap.git"
|
|
||||||
cd -
|
|
||||||
mkdir -p /tmp/tap/Formula
|
|
||||||
|
|
||||||
# Write formula
|
|
||||||
cat > /tmp/tap/Formula/core.rb << FORMULA
|
|
||||||
# typed: false
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Core < Formula
|
|
||||||
desc "Host UK development CLI"
|
|
||||||
homepage "https://github.com/host-uk/core"
|
|
||||||
version "${FORMULA_VERSION}"
|
|
||||||
license "EUPL-1.2"
|
|
||||||
|
|
||||||
on_macos do
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-darwin-arm64.tar.gz"
|
|
||||||
sha256 "${DARWIN_ARM64}"
|
|
||||||
end
|
|
||||||
|
|
||||||
on_linux do
|
|
||||||
if Hardware::CPU.arm?
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-linux-arm64.tar.gz"
|
|
||||||
sha256 "${LINUX_ARM64}"
|
|
||||||
else
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-linux-amd64.tar.gz"
|
|
||||||
sha256 "${LINUX_AMD64}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def install
|
|
||||||
bin.install "core"
|
|
||||||
end
|
|
||||||
|
|
||||||
test do
|
|
||||||
system "\#{bin}/core", "--version"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
FORMULA
|
|
||||||
|
|
||||||
# Remove leading whitespace from heredoc
|
|
||||||
sed -i 's/^ //' /tmp/tap/Formula/core.rb
|
|
||||||
|
|
||||||
# Read IDE checksums (may not exist if build-ide failed)
|
|
||||||
IDE_DARWIN_ARM64=$(cat dist/core-ide-darwin-arm64.tar.gz.sha256 2>/dev/null || echo "")
|
|
||||||
IDE_LINUX_AMD64=$(cat dist/core-ide-linux-amd64.tar.gz.sha256 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
# Write core-ide Formula (Linux binary)
|
|
||||||
if [ -n "${IDE_LINUX_AMD64}" ]; then
|
|
||||||
cat > /tmp/tap/Formula/core-ide.rb << FORMULA
|
|
||||||
# typed: false
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class CoreIde < Formula
|
|
||||||
desc "Host UK desktop development environment"
|
|
||||||
homepage "https://github.com/host-uk/core"
|
|
||||||
version "${FORMULA_VERSION}"
|
|
||||||
license "EUPL-1.2"
|
|
||||||
|
|
||||||
on_linux do
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-ide-linux-amd64.tar.gz"
|
|
||||||
sha256 "${IDE_LINUX_AMD64}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def install
|
|
||||||
bin.install "core-ide"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
FORMULA
|
|
||||||
sed -i 's/^ //' /tmp/tap/Formula/core-ide.rb
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Write core-ide Cask (macOS .app bundle)
|
|
||||||
if [ -n "${IDE_DARWIN_ARM64}" ]; then
|
|
||||||
mkdir -p /tmp/tap/Casks
|
|
||||||
cat > /tmp/tap/Casks/core-ide.rb << CASK
|
|
||||||
cask "core-ide" do
|
|
||||||
version "${FORMULA_VERSION}"
|
|
||||||
sha256 "${IDE_DARWIN_ARM64}"
|
|
||||||
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-ide-darwin-arm64.tar.gz"
|
|
||||||
name "Core IDE"
|
|
||||||
desc "Host UK desktop development environment"
|
|
||||||
homepage "https://github.com/host-uk/core"
|
|
||||||
|
|
||||||
app "Core IDE.app"
|
|
||||||
end
|
|
||||||
CASK
|
|
||||||
sed -i 's/^ //' /tmp/tap/Casks/core-ide.rb
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd /tmp/tap
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git add .
|
|
||||||
git diff --cached --quiet && echo "No changes to tap" && exit 0
|
|
||||||
git commit -m "Update core to ${FORMULA_VERSION}"
|
|
||||||
git push
|
|
||||||
|
|
||||||
update-scoop:
|
|
||||||
needs: release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Generate checksums
|
|
||||||
run: |
|
|
||||||
cd dist
|
|
||||||
for f in *.zip; do
|
|
||||||
[ -f "$f" ] || continue
|
|
||||||
sha256sum "$f" | awk '{print $1}' > "${f}.sha256"
|
|
||||||
done
|
|
||||||
echo "=== Checksums ==="
|
|
||||||
cat *.sha256 2>/dev/null || echo "No zip checksums"
|
|
||||||
|
|
||||||
- name: Update Scoop manifests
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
|
||||||
VERSION: ${{ needs.release.outputs.version }}
|
|
||||||
run: |
|
|
||||||
# Strip leading 'v' for manifest version
|
|
||||||
MANIFEST_VERSION="${VERSION#v}"
|
|
||||||
|
|
||||||
# Read checksums
|
|
||||||
WIN_AMD64=$(cat dist/core-windows-amd64.zip.sha256 2>/dev/null || echo "")
|
|
||||||
IDE_WIN_AMD64=$(cat dist/core-ide-windows-amd64.zip.sha256 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
# Clone scoop bucket
|
|
||||||
gh repo clone host-uk/scoop-bucket /tmp/scoop -- --depth=1
|
|
||||||
cd /tmp/scoop
|
|
||||||
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/host-uk/scoop-bucket.git"
|
|
||||||
|
|
||||||
# Write core.json manifest
|
|
||||||
cat > core.json << 'MANIFEST'
|
|
||||||
{
|
|
||||||
"version": "VERSION_PLACEHOLDER",
|
|
||||||
"description": "Host UK development CLI",
|
|
||||||
"homepage": "https://github.com/host-uk/core",
|
|
||||||
"license": "EUPL-1.2",
|
|
||||||
"architecture": {
|
|
||||||
"64bit": {
|
|
||||||
"url": "URL_PLACEHOLDER",
|
|
||||||
"hash": "HASH_PLACEHOLDER",
|
|
||||||
"bin": "core.exe"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"checkver": "github",
|
|
||||||
"autoupdate": {
|
|
||||||
"architecture": {
|
|
||||||
"64bit": {
|
|
||||||
"url": "https://github.com/host-uk/core/releases/download/v$version/core-windows-amd64.zip"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
sed -i "s|VERSION_PLACEHOLDER|${MANIFEST_VERSION}|g" core.json
|
|
||||||
sed -i "s|URL_PLACEHOLDER|https://github.com/host-uk/core/releases/download/${VERSION}/core-windows-amd64.zip|g" core.json
|
|
||||||
sed -i "s|HASH_PLACEHOLDER|${WIN_AMD64}|g" core.json
|
|
||||||
sed -i 's/^ //' core.json
|
|
||||||
|
|
||||||
# Write core-ide.json manifest
|
|
||||||
if [ -n "${IDE_WIN_AMD64}" ]; then
|
|
||||||
cat > core-ide.json << 'MANIFEST'
|
|
||||||
{
|
|
||||||
"version": "VERSION_PLACEHOLDER",
|
|
||||||
"description": "Host UK desktop development environment",
|
|
||||||
"homepage": "https://github.com/host-uk/core",
|
|
||||||
"license": "EUPL-1.2",
|
|
||||||
"architecture": {
|
|
||||||
"64bit": {
|
|
||||||
"url": "URL_PLACEHOLDER",
|
|
||||||
"hash": "HASH_PLACEHOLDER",
|
|
||||||
"bin": "core-ide.exe"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"checkver": "github",
|
|
||||||
"autoupdate": {
|
|
||||||
"architecture": {
|
|
||||||
"64bit": {
|
|
||||||
"url": "https://github.com/host-uk/core/releases/download/v$version/core-ide-windows-amd64.zip"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MANIFEST
|
|
||||||
sed -i "s|VERSION_PLACEHOLDER|${MANIFEST_VERSION}|g" core-ide.json
|
|
||||||
sed -i "s|URL_PLACEHOLDER|https://github.com/host-uk/core/releases/download/${VERSION}/core-ide-windows-amd64.zip|g" core-ide.json
|
|
||||||
sed -i "s|HASH_PLACEHOLDER|${IDE_WIN_AMD64}|g" core-ide.json
|
|
||||||
sed -i 's/^ //' core-ide.json
|
|
||||||
fi
|
|
||||||
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git add .
|
|
||||||
git diff --cached --quiet && echo "No changes to scoop bucket" && exit 0
|
|
||||||
git commit -m "Update core to ${MANIFEST_VERSION}"
|
|
||||||
git push
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issues
|
|
||||||
name: "Auto Label: Issue Created/Edited"
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened, edited]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
issues: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
label:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Auto-label based on content
|
|
||||||
uses: actions/github-script@v8
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const issue = context.payload.issue;
|
|
||||||
const title = issue.title.toLowerCase();
|
|
||||||
const body = (issue.body || '').toLowerCase();
|
|
||||||
const content = title + ' ' + body;
|
|
||||||
|
|
||||||
const labelsToAdd = [];
|
|
||||||
|
|
||||||
// Type labels based on title prefix
|
|
||||||
if (title.includes('[bug]')) {
|
|
||||||
labelsToAdd.push('bug');
|
|
||||||
} else if (title.includes('[feature]') || title.includes('feat(') || title.includes('feat:')) {
|
|
||||||
labelsToAdd.push('enhancement');
|
|
||||||
} else if (title.includes('[docs]') || title.includes('docs(') || title.includes('docs:')) {
|
|
||||||
labelsToAdd.push('documentation');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Project labels based on content
|
|
||||||
if (content.includes('core dev') || content.includes('core work') || content.includes('core commit') || content.includes('core push')) {
|
|
||||||
labelsToAdd.push('project:core-cli');
|
|
||||||
}
|
|
||||||
if (content.includes('core php') || content.includes('composer') || content.includes('pest') || content.includes('phpstan')) {
|
|
||||||
labelsToAdd.push('project:core-php');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Language labels
|
|
||||||
if (content.includes('.go') || content.includes('golang') || content.includes('go mod')) {
|
|
||||||
labelsToAdd.push('go');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Priority detection
|
|
||||||
if (content.includes('critical') || content.includes('urgent') || content.includes('breaking')) {
|
|
||||||
labelsToAdd.push('priority:high');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Agent labels
|
|
||||||
if (content.includes('agent') || content.includes('ai ') || content.includes('claude') || content.includes('agentic')) {
|
|
||||||
labelsToAdd.push('agentic');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Complexity - from template dropdown or heuristics
|
|
||||||
if (body.includes('small - quick fix')) {
|
|
||||||
labelsToAdd.push('complexity:small');
|
|
||||||
labelsToAdd.push('good first issue');
|
|
||||||
} else if (body.includes('medium - multiple files')) {
|
|
||||||
labelsToAdd.push('complexity:medium');
|
|
||||||
} else if (body.includes('large - significant')) {
|
|
||||||
labelsToAdd.push('complexity:large');
|
|
||||||
} else if (!body.includes('unknown - not sure')) {
|
|
||||||
// Heuristic complexity detection
|
|
||||||
const checklistCount = (body.match(/- \[ \]/g) || []).length;
|
|
||||||
const codeBlocks = (body.match(/```/g) || []).length / 2;
|
|
||||||
const sections = (body.match(/^##/gm) || []).length;
|
|
||||||
const fileRefs = (body.match(/\.(go|php|js|ts|yml|yaml|json|md)\b/g) || []).length;
|
|
||||||
|
|
||||||
const complexKeywords = ['refactor', 'rewrite', 'migration', 'breaking change', 'across repos', 'architecture'];
|
|
||||||
const simpleKeywords = ['simple', 'quick fix', 'typo', 'minor', 'trivial'];
|
|
||||||
|
|
||||||
const hasComplexKeyword = complexKeywords.some(k => content.includes(k));
|
|
||||||
const hasSimpleKeyword = simpleKeywords.some(k => content.includes(k));
|
|
||||||
|
|
||||||
let score = checklistCount * 2 + codeBlocks + sections + fileRefs;
|
|
||||||
score += hasComplexKeyword ? 5 : 0;
|
|
||||||
score -= hasSimpleKeyword ? 3 : 0;
|
|
||||||
|
|
||||||
if (hasSimpleKeyword || score <= 2) {
|
|
||||||
labelsToAdd.push('complexity:small');
|
|
||||||
labelsToAdd.push('good first issue');
|
|
||||||
} else if (score <= 6) {
|
|
||||||
labelsToAdd.push('complexity:medium');
|
|
||||||
} else {
|
|
||||||
labelsToAdd.push('complexity:large');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Apply labels if any detected
|
|
||||||
if (labelsToAdd.length > 0) {
|
|
||||||
// Filter to only existing labels
|
|
||||||
const existingLabels = await github.rest.issues.listLabelsForRepo({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
per_page: 100
|
|
||||||
});
|
|
||||||
const validLabels = existingLabels.data.map(l => l.name);
|
|
||||||
const filteredLabels = labelsToAdd.filter(l => validLabels.includes(l));
|
|
||||||
|
|
||||||
if (filteredLabels.length > 0) {
|
|
||||||
await github.rest.issues.addLabels({
|
|
||||||
owner: context.repo.owner,
|
|
||||||
repo: context.repo.repo,
|
|
||||||
issue_number: issue.number,
|
|
||||||
labels: filteredLabels
|
|
||||||
});
|
|
||||||
console.log(`Added labels: ${filteredLabels.join(', ')}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
name: Auto Merge
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, reopened, ready_for_review]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
env:
|
|
||||||
GH_REPO: ${{ github.repository }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
merge:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: github.event.pull_request.draft == false
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
- name: Enable auto-merge
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
env:
|
|
||||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
with:
|
|
||||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
script: |
|
|
||||||
const author = context.payload.pull_request.user.login;
|
|
||||||
const association = context.payload.pull_request.author_association;
|
|
||||||
|
|
||||||
// Trusted bot accounts (act as org members)
|
|
||||||
const trustedBots = ['google-labs-jules[bot]'];
|
|
||||||
const isTrustedBot = trustedBots.includes(author);
|
|
||||||
|
|
||||||
// Check author association from webhook payload
|
|
||||||
const trusted = ['MEMBER', 'OWNER', 'COLLABORATOR'];
|
|
||||||
if (!isTrustedBot && !trusted.includes(association)) {
|
|
||||||
core.info(`${author} is ${association} — skipping auto-merge`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await exec.exec('gh', [
|
|
||||||
'pr', 'merge', process.env.PR_NUMBER,
|
|
||||||
'--auto',
|
|
||||||
'--merge',
|
|
||||||
'-R', `${context.repo.owner}/${context.repo.repo}`
|
|
||||||
]);
|
|
||||||
core.info(`Auto-merge enabled for #${process.env.PR_NUMBER}`);
|
|
||||||
} catch (error) {
|
|
||||||
core.error(`Failed to enable auto-merge: ${error.message}`);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
||||||
name: Auto Project
|
|
||||||
|
|
||||||
on:
|
|
||||||
issues:
|
|
||||||
types: [opened, labeled]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
project:
|
|
||||||
uses: host-uk/.github/.github/workflows/auto-project.yml@main
|
|
||||||
secrets: inherit
|
|
||||||
|
|
@ -1,309 +0,0 @@
|
||||||
# BugSETI Release Workflow
|
|
||||||
# Builds for all platforms and creates GitHub releases
|
|
||||||
name: "BugSETI Release"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'bugseti-v*.*.*' # Stable: bugseti-v1.0.0
|
|
||||||
- 'bugseti-v*.*.*-beta.*' # Beta: bugseti-v1.0.0-beta.1
|
|
||||||
- 'bugseti-nightly-*' # Nightly: bugseti-nightly-20260205
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
env:
|
|
||||||
APP_NAME: bugseti
|
|
||||||
WAILS_VERSION: "3"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# Determine release channel from tag
|
|
||||||
prepare:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
version: ${{ steps.version.outputs.version }}
|
|
||||||
channel: ${{ steps.version.outputs.channel }}
|
|
||||||
prerelease: ${{ steps.version.outputs.prerelease }}
|
|
||||||
steps:
|
|
||||||
- name: Determine version and channel
|
|
||||||
id: version
|
|
||||||
env:
|
|
||||||
TAG: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
if [[ "$TAG" == bugseti-nightly-* ]]; then
|
|
||||||
VERSION="${TAG#bugseti-}"
|
|
||||||
CHANNEL="nightly"
|
|
||||||
PRERELEASE="true"
|
|
||||||
elif [[ "$TAG" == *-beta.* ]]; then
|
|
||||||
VERSION="${TAG#bugseti-v}"
|
|
||||||
CHANNEL="beta"
|
|
||||||
PRERELEASE="true"
|
|
||||||
else
|
|
||||||
VERSION="${TAG#bugseti-v}"
|
|
||||||
CHANNEL="stable"
|
|
||||||
PRERELEASE="false"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "channel=${CHANNEL}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "prerelease=${PRERELEASE}" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
echo "Tag: $TAG"
|
|
||||||
echo "Version: $VERSION"
|
|
||||||
echo "Channel: $CHANNEL"
|
|
||||||
echo "Prerelease: $PRERELEASE"
|
|
||||||
|
|
||||||
build:
|
|
||||||
needs: prepare
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# macOS ARM64 (Apple Silicon)
|
|
||||||
- os: macos-latest
|
|
||||||
goos: darwin
|
|
||||||
goarch: arm64
|
|
||||||
ext: ""
|
|
||||||
archive: tar.gz
|
|
||||||
# macOS AMD64 (Intel)
|
|
||||||
- os: macos-13
|
|
||||||
goos: darwin
|
|
||||||
goarch: amd64
|
|
||||||
ext: ""
|
|
||||||
archive: tar.gz
|
|
||||||
# Linux AMD64
|
|
||||||
- os: ubuntu-latest
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
ext: ""
|
|
||||||
archive: tar.gz
|
|
||||||
# Linux ARM64
|
|
||||||
- os: ubuntu-24.04-arm
|
|
||||||
goos: linux
|
|
||||||
goarch: arm64
|
|
||||||
ext: ""
|
|
||||||
archive: tar.gz
|
|
||||||
# Windows AMD64
|
|
||||||
- os: windows-latest
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
ext: ".exe"
|
|
||||||
archive: zip
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
VERSION: ${{ needs.prepare.outputs.version }}
|
|
||||||
CHANNEL: ${{ needs.prepare.outputs.channel }}
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: cmd/bugseti
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: host-uk/build/actions/setup/go@v4.0.0
|
|
||||||
with:
|
|
||||||
go-version: "1.25"
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "20"
|
|
||||||
|
|
||||||
- name: Install Wails CLI
|
|
||||||
run: go install github.com/wailsapp/wails/v3/cmd/wails3@latest
|
|
||||||
|
|
||||||
- name: Install frontend dependencies
|
|
||||||
working-directory: cmd/bugseti/frontend
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Generate bindings
|
|
||||||
run: wails3 generate bindings -f '-tags production' -clean=false -ts -i
|
|
||||||
|
|
||||||
- name: Build frontend
|
|
||||||
working-directory: cmd/bugseti/frontend
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Install Linux dependencies
|
|
||||||
if: matrix.goos == 'linux'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libayatana-appindicator3-dev
|
|
||||||
|
|
||||||
- name: Build BugSETI
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
EXT: ${{ matrix.ext }}
|
|
||||||
ARCHIVE: ${{ matrix.archive }}
|
|
||||||
COMMIT_SHA: ${{ github.sha }}
|
|
||||||
run: |
|
|
||||||
BINARY="${APP_NAME}${EXT}"
|
|
||||||
ARCHIVE_PREFIX="${APP_NAME}-${GOOS}-${GOARCH}"
|
|
||||||
|
|
||||||
BUILD_FLAGS="-tags production -trimpath -buildvcs=false"
|
|
||||||
|
|
||||||
# Version injection via ldflags
|
|
||||||
LDFLAGS="-s -w"
|
|
||||||
LDFLAGS="${LDFLAGS} -X github.com/host-uk/core/internal/bugseti.Version=${VERSION}"
|
|
||||||
LDFLAGS="${LDFLAGS} -X github.com/host-uk/core/internal/bugseti.Channel=${CHANNEL}"
|
|
||||||
LDFLAGS="${LDFLAGS} -X github.com/host-uk/core/internal/bugseti.Commit=${COMMIT_SHA}"
|
|
||||||
LDFLAGS="${LDFLAGS} -X github.com/host-uk/core/internal/bugseti.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
||||||
|
|
||||||
if [ "$GOOS" = "windows" ]; then
|
|
||||||
export CGO_ENABLED=0
|
|
||||||
LDFLAGS="${LDFLAGS} -H windowsgui"
|
|
||||||
|
|
||||||
# Generate Windows syso resource
|
|
||||||
cd build
|
|
||||||
wails3 generate syso -arch ${GOARCH} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_${GOARCH}.syso 2>/dev/null || true
|
|
||||||
cd ..
|
|
||||||
elif [ "$GOOS" = "darwin" ]; then
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
export CGO_CFLAGS="-mmacosx-version-min=10.15"
|
|
||||||
export CGO_LDFLAGS="-mmacosx-version-min=10.15"
|
|
||||||
export MACOSX_DEPLOYMENT_TARGET="10.15"
|
|
||||||
else
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
fi
|
|
||||||
|
|
||||||
mkdir -p bin
|
|
||||||
go build ${BUILD_FLAGS} -ldflags="${LDFLAGS}" -o "./bin/${BINARY}"
|
|
||||||
|
|
||||||
# Clean up syso files
|
|
||||||
rm -f *.syso
|
|
||||||
|
|
||||||
# Package based on platform
|
|
||||||
if [ "$GOOS" = "darwin" ]; then
|
|
||||||
# Create .app bundle
|
|
||||||
mkdir -p "./bin/BugSETI.app/Contents/"{MacOS,Resources}
|
|
||||||
cp build/darwin/icons.icns "./bin/BugSETI.app/Contents/Resources/" 2>/dev/null || true
|
|
||||||
cp "./bin/${BINARY}" "./bin/BugSETI.app/Contents/MacOS/"
|
|
||||||
cp build/darwin/Info.plist "./bin/BugSETI.app/Contents/"
|
|
||||||
codesign --force --deep --sign - "./bin/BugSETI.app" 2>/dev/null || true
|
|
||||||
tar czf "./bin/${ARCHIVE_PREFIX}.tar.gz" -C ./bin "BugSETI.app"
|
|
||||||
elif [ "$GOOS" = "windows" ]; then
|
|
||||||
cd ./bin && zip "${ARCHIVE_PREFIX}.zip" "${BINARY}" && cd ..
|
|
||||||
else
|
|
||||||
tar czf "./bin/${ARCHIVE_PREFIX}.tar.gz" -C ./bin "${BINARY}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rename raw binary for individual download
|
|
||||||
mv "./bin/${BINARY}" "./bin/${ARCHIVE_PREFIX}${EXT}"
|
|
||||||
|
|
||||||
# Generate checksum
|
|
||||||
cd ./bin
|
|
||||||
sha256sum "${ARCHIVE_PREFIX}.${ARCHIVE}" > "${ARCHIVE_PREFIX}.${ARCHIVE}.sha256"
|
|
||||||
sha256sum "${ARCHIVE_PREFIX}${EXT}" > "${ARCHIVE_PREFIX}${EXT}.sha256"
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: bugseti-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
||||||
path: |
|
|
||||||
cmd/bugseti/bin/bugseti-*
|
|
||||||
retention-days: 7
|
|
||||||
|
|
||||||
release:
|
|
||||||
needs: [prepare, build]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
TAG_NAME: ${{ github.ref_name }}
|
|
||||||
VERSION: ${{ needs.prepare.outputs.version }}
|
|
||||||
CHANNEL: ${{ needs.prepare.outputs.channel }}
|
|
||||||
PRERELEASE: ${{ needs.prepare.outputs.prerelease }}
|
|
||||||
REPO: ${{ github.repository }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Download all artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: List release files
|
|
||||||
run: |
|
|
||||||
echo "=== Release files ==="
|
|
||||||
ls -la dist/
|
|
||||||
echo "=== Checksums ==="
|
|
||||||
cat dist/*.sha256
|
|
||||||
|
|
||||||
- name: Create release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
# Determine release title
|
|
||||||
if [ "$CHANNEL" = "nightly" ]; then
|
|
||||||
TITLE="BugSETI Nightly (${VERSION})"
|
|
||||||
elif [ "$CHANNEL" = "beta" ]; then
|
|
||||||
TITLE="BugSETI v${VERSION} (Beta)"
|
|
||||||
else
|
|
||||||
TITLE="BugSETI v${VERSION}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create release notes
|
|
||||||
cat > release-notes.md << EOF
|
|
||||||
## BugSETI ${VERSION}
|
|
||||||
|
|
||||||
**Channel:** ${CHANNEL}
|
|
||||||
|
|
||||||
### Downloads
|
|
||||||
|
|
||||||
| Platform | Architecture | Binary | Archive |
|
|
||||||
|----------|-------------|--------|---------|
|
|
||||||
| macOS | ARM64 (Apple Silicon) | [bugseti-darwin-arm64](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-darwin-arm64) | [tar.gz](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-darwin-arm64.tar.gz) |
|
|
||||||
| macOS | AMD64 (Intel) | [bugseti-darwin-amd64](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-darwin-amd64) | [tar.gz](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-darwin-amd64.tar.gz) |
|
|
||||||
| Linux | AMD64 | [bugseti-linux-amd64](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-linux-amd64) | [tar.gz](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-linux-amd64.tar.gz) |
|
|
||||||
| Linux | ARM64 | [bugseti-linux-arm64](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-linux-arm64) | [tar.gz](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-linux-arm64.tar.gz) |
|
|
||||||
| Windows | AMD64 | [bugseti-windows-amd64.exe](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-windows-amd64.exe) | [zip](https://github.com/${REPO}/releases/download/${TAG_NAME}/bugseti-windows-amd64.zip) |
|
|
||||||
|
|
||||||
### Checksums (SHA256)
|
|
||||||
|
|
||||||
\`\`\`
|
|
||||||
$(cat dist/*.sha256)
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
---
|
|
||||||
*BugSETI - Distributed Bug Fixing, like SETI@home but for code*
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Build release command
|
|
||||||
RELEASE_ARGS=(
|
|
||||||
--title "$TITLE"
|
|
||||||
--notes-file release-notes.md
|
|
||||||
)
|
|
||||||
|
|
||||||
if [ "$PRERELEASE" = "true" ]; then
|
|
||||||
RELEASE_ARGS+=(--prerelease)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create the release
|
|
||||||
gh release create "$TAG_NAME" \
|
|
||||||
"${RELEASE_ARGS[@]}" \
|
|
||||||
dist/*
|
|
||||||
|
|
||||||
# Scheduled nightly builds
|
|
||||||
nightly:
|
|
||||||
if: github.event_name == 'schedule'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Create nightly tag
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
DATE=$(date -u +%Y%m%d)
|
|
||||||
TAG="bugseti-nightly-${DATE}"
|
|
||||||
|
|
||||||
# Delete existing nightly tag for today if it exists
|
|
||||||
gh release delete "$TAG" --yes 2>/dev/null || true
|
|
||||||
git push origin ":refs/tags/$TAG" 2>/dev/null || true
|
|
||||||
|
|
||||||
# Create new tag
|
|
||||||
git tag "$TAG"
|
|
||||||
git push origin "$TAG"
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
|
|
||||||
name: "CI: Manual"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
CORE_VERSION: dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
qa:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
|
||||||
|
|
||||||
- name: Build core CLI
|
|
||||||
run: |
|
|
||||||
go build -ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=${{ env.CORE_VERSION }}" -o /usr/local/bin/core .
|
|
||||||
core --version
|
|
||||||
|
|
||||||
- name: Generate code
|
|
||||||
run: go generate ./internal/cmd/updater/...
|
|
||||||
|
|
||||||
- name: Run QA
|
|
||||||
# Skip lint until golangci-lint supports Go 1.25
|
|
||||||
run: core go qa --skip=lint
|
|
||||||
|
|
||||||
- name: Verify build
|
|
||||||
run: |
|
|
||||||
core build --targets=linux/amd64 --ci
|
|
||||||
dist/linux_amd64/core --version
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
|
|
||||||
name: "CI: Pull Request"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [dev, main]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CORE_VERSION: dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
qa:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
|
||||||
|
|
||||||
- name: Build core CLI
|
|
||||||
run: |
|
|
||||||
go build -ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=${{ env.CORE_VERSION }}" -o /usr/local/bin/core .
|
|
||||||
core --version
|
|
||||||
|
|
||||||
- name: Generate code
|
|
||||||
run: go generate ./internal/cmd/updater/...
|
|
||||||
|
|
||||||
- name: Run QA
|
|
||||||
# Skip lint until golangci-lint supports Go 1.25
|
|
||||||
run: core go qa --skip=lint
|
|
||||||
|
|
||||||
- name: Verify build
|
|
||||||
run: |
|
|
||||||
core build --targets=linux/amd64 --ci
|
|
||||||
dist/linux_amd64/core --version
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push
|
|
||||||
name: "CI: Push"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev, main]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CORE_VERSION: dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
qa:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
|
||||||
|
|
||||||
- name: Build core CLI
|
|
||||||
run: |
|
|
||||||
go build -ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=${{ env.CORE_VERSION }}" -o /usr/local/bin/core .
|
|
||||||
core --version
|
|
||||||
|
|
||||||
- name: Generate code
|
|
||||||
run: go generate ./internal/cmd/updater/...
|
|
||||||
|
|
||||||
- name: Run QA
|
|
||||||
# Skip lint until golangci-lint supports Go 1.25
|
|
||||||
run: core go qa --skip=lint
|
|
||||||
|
|
||||||
- name: Verify build
|
|
||||||
run: |
|
|
||||||
core build --targets=linux/amd64 --ci
|
|
||||||
dist/linux_amd64/core --version
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev, main]
|
|
||||||
pull_request:
|
|
||||||
branches: [dev, main]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
CORE_VERSION: dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
qa:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
# Try 4.1 first (Ubuntu 22.04+), fall back to 4.0 (Ubuntu 20.04)
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev || \
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev
|
|
||||||
|
|
||||||
- name: Build core CLI
|
|
||||||
run: |
|
|
||||||
go build -ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=${{ env.CORE_VERSION }}" -o /usr/local/bin/core .
|
|
||||||
core --version
|
|
||||||
|
|
||||||
- name: Generate code
|
|
||||||
run: go generate ./internal/cmd/updater/...
|
|
||||||
|
|
||||||
- name: Run QA
|
|
||||||
# Skip lint until golangci-lint supports Go 1.25
|
|
||||||
run: core go qa --skip=lint
|
|
||||||
|
|
||||||
- name: Verify build
|
|
||||||
run: |
|
|
||||||
core build --targets=linux/amd64 --ci
|
|
||||||
dist/linux_amd64/core --version
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
|
|
||||||
name: "CodeQL: Pull Request"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [dev, main]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v4
|
|
||||||
with:
|
|
||||||
languages: go
|
|
||||||
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v4
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v4
|
|
||||||
with:
|
|
||||||
category: "/language:go"
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push
|
|
||||||
name: "CodeQL: Push"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev, main]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v4
|
|
||||||
with:
|
|
||||||
languages: go
|
|
||||||
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v4
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v4
|
|
||||||
with:
|
|
||||||
category: "/language:go"
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
|
|
||||||
name: "CodeQL: Schedule"
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 6 * * 1"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
analyze:
|
|
||||||
name: Analyze
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
security-events: write
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Initialize CodeQL
|
|
||||||
uses: github/codeql-action/init@v4
|
|
||||||
with:
|
|
||||||
languages: go
|
|
||||||
|
|
||||||
- name: Autobuild
|
|
||||||
uses: github/codeql-action/autobuild@v4
|
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
|
||||||
uses: github/codeql-action/analyze@v4
|
|
||||||
with:
|
|
||||||
category: "/language:go"
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
|
|
||||||
name: "Code Scanning: Pull Request"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: ["dev"]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
CodeQL:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
security-events: write
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: "Checkout Repository"
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: "Initialize CodeQL"
|
|
||||||
uses: github/codeql-action/init@v4
|
|
||||||
with:
|
|
||||||
languages: go,javascript,typescript
|
|
||||||
|
|
||||||
- name: "Autobuild"
|
|
||||||
uses: github/codeql-action/autobuild@v4
|
|
||||||
|
|
||||||
- name: "Perform CodeQL Analysis"
|
|
||||||
uses: github/codeql-action/analyze@v4
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push
|
|
||||||
name: "Code Scanning: Push"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: ["dev"]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
CodeQL:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
security-events: write
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: "Checkout Repository"
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: "Initialize CodeQL"
|
|
||||||
uses: github/codeql-action/init@v4
|
|
||||||
with:
|
|
||||||
languages: go,javascript,typescript
|
|
||||||
|
|
||||||
- name: "Autobuild"
|
|
||||||
uses: github/codeql-action/autobuild@v4
|
|
||||||
|
|
||||||
- name: "Perform CodeQL Analysis"
|
|
||||||
uses: github/codeql-action/analyze@v4
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule
|
|
||||||
name: "Code Scanning: Schedule"
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: "0 2 * * 1-5"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
CodeQL:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
security-events: write
|
|
||||||
actions: read
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: "Checkout Repository"
|
|
||||||
uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: "Initialize CodeQL"
|
|
||||||
uses: github/codeql-action/init@v4
|
|
||||||
with:
|
|
||||||
languages: go,javascript,typescript
|
|
||||||
|
|
||||||
- name: "Autobuild"
|
|
||||||
uses: github/codeql-action/autobuild@v4
|
|
||||||
|
|
||||||
- name: "Perform CodeQL Analysis"
|
|
||||||
uses: github/codeql-action/analyze@v4
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
|
|
||||||
name: "Coverage: Manual"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
CORE_VERSION: dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
coverage:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
|
||||||
|
|
||||||
- name: Build core CLI
|
|
||||||
run: |
|
|
||||||
go build -ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=${{ env.CORE_VERSION }}" -o /usr/local/bin/core .
|
|
||||||
core --version
|
|
||||||
|
|
||||||
- name: Generate code
|
|
||||||
run: go generate ./internal/cmd/updater/...
|
|
||||||
|
|
||||||
- name: Run coverage
|
|
||||||
run: core go cov
|
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov
|
|
||||||
uses: codecov/codecov-action@v5
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
|
|
||||||
- name: Upload coverage report
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
with:
|
|
||||||
name: coverage-report
|
|
||||||
path: coverage.txt
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
|
|
||||||
name: "Coverage: Pull Request"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
branches: [dev, main]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CORE_VERSION: dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
coverage:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
|
||||||
|
|
||||||
- name: Build core CLI
|
|
||||||
run: |
|
|
||||||
go build -ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=${{ env.CORE_VERSION }}" -o /usr/local/bin/core .
|
|
||||||
core --version
|
|
||||||
|
|
||||||
- name: Generate code
|
|
||||||
run: go generate ./internal/cmd/updater/...
|
|
||||||
|
|
||||||
- name: Run coverage
|
|
||||||
run: core go cov
|
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov
|
|
||||||
uses: codecov/codecov-action@v5
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
|
|
||||||
- name: Upload coverage report
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
with:
|
|
||||||
name: coverage-report
|
|
||||||
path: coverage.txt
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push
|
|
||||||
name: "Coverage: Push"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev, main]
|
|
||||||
|
|
||||||
env:
|
|
||||||
CORE_VERSION: dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
coverage:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev
|
|
||||||
|
|
||||||
- name: Build core CLI
|
|
||||||
run: |
|
|
||||||
go build -ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=${{ env.CORE_VERSION }}" -o /usr/local/bin/core .
|
|
||||||
core --version
|
|
||||||
|
|
||||||
- name: Generate code
|
|
||||||
run: go generate ./internal/cmd/updater/...
|
|
||||||
|
|
||||||
- name: Run coverage
|
|
||||||
run: core go cov
|
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov
|
|
||||||
uses: codecov/codecov-action@v5
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
|
|
||||||
- name: Upload coverage report
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
with:
|
|
||||||
name: coverage-report
|
|
||||||
path: coverage.txt
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
name: Coverage
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [dev, main]
|
|
||||||
pull_request:
|
|
||||||
branches: [dev, main]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
CORE_VERSION: dev
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
coverage:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
|
|
||||||
- name: Install system dependencies
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
# Try 4.1 first (Ubuntu 22.04+), fall back to 4.0 (Ubuntu 20.04)
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev || \
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev
|
|
||||||
|
|
||||||
- name: Build core CLI
|
|
||||||
run: |
|
|
||||||
go build -ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=${{ env.CORE_VERSION }}" -o /usr/local/bin/core .
|
|
||||||
core --version
|
|
||||||
|
|
||||||
- name: Generate code
|
|
||||||
run: go generate ./internal/cmd/updater/...
|
|
||||||
|
|
||||||
- name: Run coverage
|
|
||||||
run: core go cov --output coverage.txt --threshold 40 --branch-threshold 35
|
|
||||||
|
|
||||||
- name: Upload coverage reports to Codecov
|
|
||||||
uses: codecov/codecov-action@v5
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
|
|
||||||
- name: Upload coverage report
|
|
||||||
uses: actions/upload-artifact@v6
|
|
||||||
with:
|
|
||||||
name: coverage-report
|
|
||||||
path: coverage.txt
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#workflow_dispatch
|
|
||||||
name: "PR Build: Manual"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
pr_number:
|
|
||||||
description: 'PR number to build'
|
|
||||||
required: true
|
|
||||||
type: number
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
NEXT_VERSION: "0.0.4"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
platform: linux/amd64
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
uses: host-uk/build@v3
|
|
||||||
with:
|
|
||||||
build-name: core
|
|
||||||
build-platform: ${{ matrix.platform }}
|
|
||||||
build: true
|
|
||||||
package: true
|
|
||||||
sign: false
|
|
||||||
|
|
||||||
draft-release:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
PR_NUM: ${{ inputs.pr_number }}
|
|
||||||
PR_SHA: ${{ github.sha }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Prepare release files
|
|
||||||
run: |
|
|
||||||
mkdir -p release
|
|
||||||
cp dist/* release/ 2>/dev/null || true
|
|
||||||
ls -la release/
|
|
||||||
|
|
||||||
- name: Create draft release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
TAG="v${{ env.NEXT_VERSION }}.pr.${PR_NUM}.bid.${{ github.run_id }}"
|
|
||||||
|
|
||||||
# Delete existing draft for this PR if it exists
|
|
||||||
gh release delete "$TAG" -y 2>/dev/null || true
|
|
||||||
git push origin ":refs/tags/$TAG" 2>/dev/null || true
|
|
||||||
|
|
||||||
gh release create "$TAG" \
|
|
||||||
--title "Draft: PR #${PR_NUM}" \
|
|
||||||
--notes "Draft build for PR #${PR_NUM}.
|
|
||||||
|
|
||||||
**Version:** $TAG
|
|
||||||
**PR:** #${PR_NUM}
|
|
||||||
**Commit:** ${PR_SHA}
|
|
||||||
**Built:** $(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
|
||||||
**Run:** ${{ github.run_id }}
|
|
||||||
|
|
||||||
## Channel: Draft
|
|
||||||
|
|
||||||
This is a draft build for testing PR changes before merge.
|
|
||||||
Not intended for production use.
|
|
||||||
|
|
||||||
Build artifacts available for download and testing.
|
|
||||||
" \
|
|
||||||
--draft \
|
|
||||||
--prerelease \
|
|
||||||
release/*
|
|
||||||
|
|
@ -1,89 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request
|
|
||||||
name: "PR Build: Pull Request"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
NEXT_VERSION: "0.0.4"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
# Only build if PR is from the same repo (not forks)
|
|
||||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
platform: linux/amd64
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
|
||||||
|
|
||||||
- name: Build
|
|
||||||
uses: host-uk/build@v3
|
|
||||||
with:
|
|
||||||
build-name: core
|
|
||||||
build-platform: ${{ matrix.platform }}
|
|
||||||
build: true
|
|
||||||
package: true
|
|
||||||
sign: false
|
|
||||||
|
|
||||||
draft-release:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
PR_NUM: ${{ github.event.pull_request.number }}
|
|
||||||
PR_SHA: ${{ github.event.pull_request.head.sha }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Prepare release files
|
|
||||||
run: |
|
|
||||||
mkdir -p release
|
|
||||||
cp dist/* release/ 2>/dev/null || true
|
|
||||||
ls -la release/
|
|
||||||
|
|
||||||
- name: Create draft release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
TAG="v${{ env.NEXT_VERSION }}.pr.${PR_NUM}.bid.${{ github.run_id }}"
|
|
||||||
|
|
||||||
# Delete existing draft for this PR if it exists
|
|
||||||
gh release delete "$TAG" -y 2>/dev/null || true
|
|
||||||
git push origin ":refs/tags/$TAG" 2>/dev/null || true
|
|
||||||
|
|
||||||
gh release create "$TAG" \
|
|
||||||
--title "Draft: PR #${PR_NUM}" \
|
|
||||||
--notes "Draft build for PR #${PR_NUM}.
|
|
||||||
|
|
||||||
**Version:** $TAG
|
|
||||||
**PR:** #${PR_NUM}
|
|
||||||
**Commit:** ${PR_SHA}
|
|
||||||
**Built:** $(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
|
||||||
**Run:** ${{ github.run_id }}
|
|
||||||
|
|
||||||
## Channel: Draft
|
|
||||||
|
|
||||||
This is a draft build for testing PR changes before merge.
|
|
||||||
Not intended for production use.
|
|
||||||
|
|
||||||
Build artifacts available for download and testing.
|
|
||||||
" \
|
|
||||||
--draft \
|
|
||||||
--prerelease \
|
|
||||||
release/*
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
name: PR Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened]
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
pr_number:
|
|
||||||
description: 'PR number to build'
|
|
||||||
required: true
|
|
||||||
type: number
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
# Next version - update when releasing
|
|
||||||
NEXT_VERSION: "0.0.4"
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
# Only build if PR is from the same repo (not forks) or manually triggered
|
|
||||||
if: github.event.pull_request.head.repo.full_name == github.repository || github.event_name == 'workflow_dispatch'
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
||||||
|
|
||||||
# GUI build disabled until build action supports Wails v3
|
|
||||||
# - name: Wails Build Action
|
|
||||||
# uses: host-uk/build@v4.0.0
|
|
||||||
# with:
|
|
||||||
# build-name: core
|
|
||||||
# build-platform: ${{ matrix.goos }}/${{ matrix.goarch }}
|
|
||||||
# build: true
|
|
||||||
# package: true
|
|
||||||
# sign: false
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: host-uk/build/actions/setup/go@v4.0.0
|
|
||||||
with:
|
|
||||||
go-version: "1.25"
|
|
||||||
|
|
||||||
- name: Build CLI
|
|
||||||
run: go build -o ./bin/core .
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: core-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
||||||
path: ./bin/core
|
|
||||||
|
|
||||||
draft-release:
|
|
||||||
needs: build
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
env:
|
|
||||||
# Safe: PR number is numeric, not user-controlled string
|
|
||||||
PR_NUM: ${{ github.event.pull_request.number || inputs.pr_number }}
|
|
||||||
PR_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Prepare release files
|
|
||||||
run: |
|
|
||||||
mkdir -p release
|
|
||||||
cp dist/* release/ 2>/dev/null || true
|
|
||||||
ls -la release/
|
|
||||||
|
|
||||||
- name: Create draft release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
# Use dots for build metadata (semver v1 compatible)
|
|
||||||
TAG="v${{ env.NEXT_VERSION }}.pr.${PR_NUM}.bid.${{ github.run_id }}"
|
|
||||||
|
|
||||||
# Delete existing draft for this PR if it exists
|
|
||||||
gh release delete "$TAG" -y 2>/dev/null || true
|
|
||||||
git push origin ":refs/tags/$TAG" 2>/dev/null || true
|
|
||||||
|
|
||||||
gh release create "$TAG" \
|
|
||||||
--title "Draft: PR #${PR_NUM}" \
|
|
||||||
--notes "Draft build for PR #${PR_NUM}.
|
|
||||||
|
|
||||||
**Version:** $TAG
|
|
||||||
**PR:** #${PR_NUM}
|
|
||||||
**Commit:** ${PR_SHA}
|
|
||||||
**Built:** $(date -u +'%Y-%m-%d %H:%M:%S UTC')
|
|
||||||
**Run:** ${{ github.run_id }}
|
|
||||||
|
|
||||||
## Channel: Draft
|
|
||||||
|
|
||||||
This is a draft build for testing PR changes before merge.
|
|
||||||
Not intended for production use.
|
|
||||||
|
|
||||||
Build artifacts available for download and testing.
|
|
||||||
" \
|
|
||||||
--draft \
|
|
||||||
--prerelease \
|
|
||||||
release/*
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
name: PR Gate
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request_target:
|
|
||||||
types: [opened, synchronize, reopened, labeled]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pull-requests: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
org-gate:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Check org membership or approval label
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const author = context.payload.pull_request.user.login;
|
|
||||||
const association = context.payload.pull_request.author_association;
|
|
||||||
|
|
||||||
// Trusted accounts
|
|
||||||
const trustedAuthors = ['google-labs-jules[bot]', 'Snider'];
|
|
||||||
if (trustedAuthors.includes(author)) {
|
|
||||||
core.info(`${author} is trusted — gate passed`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check author association
|
|
||||||
const trustedAssociations = ['MEMBER', 'OWNER', 'COLLABORATOR'];
|
|
||||||
if (trustedAssociations.includes(association)) {
|
|
||||||
core.info(`${author} is ${association} — gate passed`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for external-approved label
|
|
||||||
const labels = context.payload.pull_request.labels.map(l => l.name);
|
|
||||||
if (labels.includes('external-approved')) {
|
|
||||||
core.info('external-approved label present — gate passed');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
core.setFailed(
|
|
||||||
`External PR from ${author} requires an org member to add the "external-approved" label before merge.`
|
|
||||||
);
|
|
||||||
|
|
@ -1,454 +0,0 @@
|
||||||
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#push
|
|
||||||
name: "Release: Tag Push"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*.*.*'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
- os: ubuntu-latest
|
|
||||||
goos: linux
|
|
||||||
goarch: arm64
|
|
||||||
- os: macos-latest
|
|
||||||
goos: darwin
|
|
||||||
goarch: arm64
|
|
||||||
- os: windows-latest
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: host-uk/build/actions/setup/go@v4.0.0
|
|
||||||
with:
|
|
||||||
go-version: "1.25"
|
|
||||||
|
|
||||||
- name: Build CLI
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
EXT=""
|
|
||||||
if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi
|
|
||||||
BINARY="core${EXT}"
|
|
||||||
ARCHIVE_PREFIX="core-${GOOS}-${GOARCH}"
|
|
||||||
|
|
||||||
APP_VERSION="${GITHUB_REF_NAME#v}"
|
|
||||||
go build -ldflags "-s -w -X github.com/host-uk/core/pkg/cli.AppVersion=${APP_VERSION}" -o "./bin/${BINARY}" .
|
|
||||||
|
|
||||||
# Create tar.gz for Homebrew (non-Windows)
|
|
||||||
if [ "$GOOS" != "windows" ]; then
|
|
||||||
tar czf "./bin/${ARCHIVE_PREFIX}.tar.gz" -C ./bin "${BINARY}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create zip for Scoop (Windows)
|
|
||||||
if [ "$GOOS" = "windows" ]; then
|
|
||||||
cd ./bin && zip "${ARCHIVE_PREFIX}.zip" "${BINARY}" && cd ..
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rename raw binary to platform-specific name for release
|
|
||||||
mv "./bin/${BINARY}" "./bin/${ARCHIVE_PREFIX}${EXT}"
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: core-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
||||||
path: ./bin/core-*
|
|
||||||
|
|
||||||
build-ide:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: macos-latest
|
|
||||||
goos: darwin
|
|
||||||
goarch: arm64
|
|
||||||
- os: ubuntu-latest
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
- os: windows-latest
|
|
||||||
goos: windows
|
|
||||||
goarch: amd64
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
env:
|
|
||||||
GOOS: ${{ matrix.goos }}
|
|
||||||
GOARCH: ${{ matrix.goarch }}
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: internal/core-ide
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Setup Go
|
|
||||||
uses: host-uk/build/actions/setup/go@v4.0.0
|
|
||||||
with:
|
|
||||||
go-version: "1.25"
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: "20"
|
|
||||||
|
|
||||||
- name: Install Wails CLI
|
|
||||||
run: go install github.com/wailsapp/wails/v3/cmd/wails3@latest
|
|
||||||
|
|
||||||
- name: Install frontend dependencies
|
|
||||||
working-directory: internal/core-ide/frontend
|
|
||||||
run: npm ci
|
|
||||||
|
|
||||||
- name: Generate bindings
|
|
||||||
run: wails3 generate bindings -f '-tags production' -clean=false -ts -i
|
|
||||||
|
|
||||||
- name: Build frontend
|
|
||||||
working-directory: internal/core-ide/frontend
|
|
||||||
run: npm run build
|
|
||||||
|
|
||||||
- name: Install Linux dependencies
|
|
||||||
if: matrix.goos == 'linux'
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev
|
|
||||||
|
|
||||||
- name: Build IDE
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
EXT=""
|
|
||||||
if [ "$GOOS" = "windows" ]; then EXT=".exe"; fi
|
|
||||||
BINARY="core-ide${EXT}"
|
|
||||||
ARCHIVE_PREFIX="core-ide-${GOOS}-${GOARCH}"
|
|
||||||
|
|
||||||
BUILD_FLAGS="-tags production -trimpath -buildvcs=false"
|
|
||||||
|
|
||||||
if [ "$GOOS" = "windows" ]; then
|
|
||||||
# Windows: no CGO, use windowsgui linker flag
|
|
||||||
export CGO_ENABLED=0
|
|
||||||
LDFLAGS="-w -s -H windowsgui"
|
|
||||||
|
|
||||||
# Generate Windows syso resource
|
|
||||||
cd build
|
|
||||||
wails3 generate syso -arch ${GOARCH} -icon windows/icon.ico -manifest windows/wails.exe.manifest -info windows/info.json -out ../wails_windows_${GOARCH}.syso
|
|
||||||
cd ..
|
|
||||||
elif [ "$GOOS" = "darwin" ]; then
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
export CGO_CFLAGS="-mmacosx-version-min=10.15"
|
|
||||||
export CGO_LDFLAGS="-mmacosx-version-min=10.15"
|
|
||||||
export MACOSX_DEPLOYMENT_TARGET="10.15"
|
|
||||||
LDFLAGS="-w -s"
|
|
||||||
else
|
|
||||||
export CGO_ENABLED=1
|
|
||||||
LDFLAGS="-w -s"
|
|
||||||
fi
|
|
||||||
|
|
||||||
go build ${BUILD_FLAGS} -ldflags="${LDFLAGS}" -o "./bin/${BINARY}"
|
|
||||||
|
|
||||||
# Clean up syso files
|
|
||||||
rm -f *.syso
|
|
||||||
|
|
||||||
# Package
|
|
||||||
if [ "$GOOS" = "darwin" ]; then
|
|
||||||
# Create .app bundle
|
|
||||||
mkdir -p "./bin/Core IDE.app/Contents/"{MacOS,Resources}
|
|
||||||
cp build/darwin/icons.icns "./bin/Core IDE.app/Contents/Resources/"
|
|
||||||
cp "./bin/${BINARY}" "./bin/Core IDE.app/Contents/MacOS/"
|
|
||||||
cp build/darwin/Info.plist "./bin/Core IDE.app/Contents/"
|
|
||||||
codesign --force --deep --sign - "./bin/Core IDE.app"
|
|
||||||
tar czf "./bin/${ARCHIVE_PREFIX}.tar.gz" -C ./bin "Core IDE.app"
|
|
||||||
elif [ "$GOOS" = "windows" ]; then
|
|
||||||
cd ./bin && zip "${ARCHIVE_PREFIX}.zip" "${BINARY}" && cd ..
|
|
||||||
else
|
|
||||||
tar czf "./bin/${ARCHIVE_PREFIX}.tar.gz" -C ./bin "${BINARY}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Rename raw binary
|
|
||||||
mv "./bin/${BINARY}" "./bin/${ARCHIVE_PREFIX}${EXT}"
|
|
||||||
|
|
||||||
- name: Upload artifact
|
|
||||||
uses: actions/upload-artifact@v4
|
|
||||||
with:
|
|
||||||
name: core-ide-${{ matrix.goos }}-${{ matrix.goarch }}
|
|
||||||
path: internal/core-ide/bin/core-ide-*
|
|
||||||
|
|
||||||
release:
|
|
||||||
needs: [build, build-ide]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
version: ${{ steps.version.outputs.version }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Set version
|
|
||||||
id: version
|
|
||||||
run: echo "version=${{ github.ref_name }}" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Prepare release files
|
|
||||||
run: |
|
|
||||||
mkdir -p release
|
|
||||||
cp dist/* release/ 2>/dev/null || true
|
|
||||||
ls -la release/
|
|
||||||
|
|
||||||
- name: Create release
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
TAG_NAME: ${{ github.ref_name }}
|
|
||||||
run: |
|
|
||||||
gh release create "$TAG_NAME" \
|
|
||||||
--title "Release $TAG_NAME" \
|
|
||||||
--generate-notes \
|
|
||||||
release/*
|
|
||||||
|
|
||||||
update-tap:
|
|
||||||
needs: release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Generate checksums
|
|
||||||
run: |
|
|
||||||
cd dist
|
|
||||||
for f in *.tar.gz; do
|
|
||||||
sha256sum "$f" | awk '{print $1}' > "${f}.sha256"
|
|
||||||
done
|
|
||||||
echo "=== Checksums ==="
|
|
||||||
cat *.sha256
|
|
||||||
|
|
||||||
- name: Update Homebrew formula
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
|
||||||
VERSION: ${{ needs.release.outputs.version }}
|
|
||||||
run: |
|
|
||||||
# Strip leading 'v' for formula version
|
|
||||||
FORMULA_VERSION="${VERSION#v}"
|
|
||||||
|
|
||||||
# Read checksums
|
|
||||||
DARWIN_ARM64=$(cat dist/core-darwin-arm64.tar.gz.sha256)
|
|
||||||
LINUX_AMD64=$(cat dist/core-linux-amd64.tar.gz.sha256)
|
|
||||||
LINUX_ARM64=$(cat dist/core-linux-arm64.tar.gz.sha256)
|
|
||||||
|
|
||||||
# Clone tap repo (configure auth for push)
|
|
||||||
gh repo clone host-uk/homebrew-tap /tmp/tap -- --depth=1
|
|
||||||
cd /tmp/tap
|
|
||||||
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/host-uk/homebrew-tap.git"
|
|
||||||
cd -
|
|
||||||
mkdir -p /tmp/tap/Formula
|
|
||||||
|
|
||||||
# Write formula
|
|
||||||
cat > /tmp/tap/Formula/core.rb << FORMULA
|
|
||||||
# typed: false
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class Core < Formula
|
|
||||||
desc "Host UK development CLI"
|
|
||||||
homepage "https://github.com/host-uk/core"
|
|
||||||
version "${FORMULA_VERSION}"
|
|
||||||
license "EUPL-1.2"
|
|
||||||
|
|
||||||
on_macos do
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-darwin-arm64.tar.gz"
|
|
||||||
sha256 "${DARWIN_ARM64}"
|
|
||||||
end
|
|
||||||
|
|
||||||
on_linux do
|
|
||||||
if Hardware::CPU.arm?
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-linux-arm64.tar.gz"
|
|
||||||
sha256 "${LINUX_ARM64}"
|
|
||||||
else
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-linux-amd64.tar.gz"
|
|
||||||
sha256 "${LINUX_AMD64}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def install
|
|
||||||
bin.install "core"
|
|
||||||
end
|
|
||||||
|
|
||||||
test do
|
|
||||||
system "\#{bin}/core", "--version"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
FORMULA
|
|
||||||
|
|
||||||
# Remove leading whitespace from heredoc
|
|
||||||
sed -i 's/^ //' /tmp/tap/Formula/core.rb
|
|
||||||
|
|
||||||
# Read IDE checksums (may not exist if build-ide failed)
|
|
||||||
IDE_DARWIN_ARM64=$(cat dist/core-ide-darwin-arm64.tar.gz.sha256 2>/dev/null || echo "")
|
|
||||||
IDE_LINUX_AMD64=$(cat dist/core-ide-linux-amd64.tar.gz.sha256 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
# Write core-ide Formula (Linux binary)
|
|
||||||
if [ -n "${IDE_LINUX_AMD64}" ]; then
|
|
||||||
cat > /tmp/tap/Formula/core-ide.rb << FORMULA
|
|
||||||
# typed: false
|
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
class CoreIde < Formula
|
|
||||||
desc "Host UK desktop development environment"
|
|
||||||
homepage "https://github.com/host-uk/core"
|
|
||||||
version "${FORMULA_VERSION}"
|
|
||||||
license "EUPL-1.2"
|
|
||||||
|
|
||||||
on_linux do
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-ide-linux-amd64.tar.gz"
|
|
||||||
sha256 "${IDE_LINUX_AMD64}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def install
|
|
||||||
bin.install "core-ide"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
FORMULA
|
|
||||||
sed -i 's/^ //' /tmp/tap/Formula/core-ide.rb
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Write core-ide Cask (macOS .app bundle)
|
|
||||||
if [ -n "${IDE_DARWIN_ARM64}" ]; then
|
|
||||||
mkdir -p /tmp/tap/Casks
|
|
||||||
cat > /tmp/tap/Casks/core-ide.rb << CASK
|
|
||||||
cask "core-ide" do
|
|
||||||
version "${FORMULA_VERSION}"
|
|
||||||
sha256 "${IDE_DARWIN_ARM64}"
|
|
||||||
|
|
||||||
url "https://github.com/host-uk/core/releases/download/${VERSION}/core-ide-darwin-arm64.tar.gz"
|
|
||||||
name "Core IDE"
|
|
||||||
desc "Host UK desktop development environment"
|
|
||||||
homepage "https://github.com/host-uk/core"
|
|
||||||
|
|
||||||
app "Core IDE.app"
|
|
||||||
end
|
|
||||||
CASK
|
|
||||||
sed -i 's/^ //' /tmp/tap/Casks/core-ide.rb
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd /tmp/tap
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git add .
|
|
||||||
git diff --cached --quiet && echo "No changes to tap" && exit 0
|
|
||||||
git commit -m "Update core to ${FORMULA_VERSION}"
|
|
||||||
git push
|
|
||||||
|
|
||||||
update-scoop:
|
|
||||||
needs: release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Download artifacts
|
|
||||||
uses: actions/download-artifact@v7
|
|
||||||
with:
|
|
||||||
path: dist
|
|
||||||
merge-multiple: true
|
|
||||||
|
|
||||||
- name: Generate checksums
|
|
||||||
run: |
|
|
||||||
cd dist
|
|
||||||
for f in *.zip; do
|
|
||||||
[ -f "$f" ] || continue
|
|
||||||
sha256sum "$f" | awk '{print $1}' > "${f}.sha256"
|
|
||||||
done
|
|
||||||
echo "=== Checksums ==="
|
|
||||||
cat *.sha256 2>/dev/null || echo "No zip checksums"
|
|
||||||
|
|
||||||
- name: Update Scoop manifests
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
|
|
||||||
VERSION: ${{ needs.release.outputs.version }}
|
|
||||||
run: |
|
|
||||||
# Strip leading 'v' for manifest version
|
|
||||||
MANIFEST_VERSION="${VERSION#v}"
|
|
||||||
|
|
||||||
# Read checksums
|
|
||||||
WIN_AMD64=$(cat dist/core-windows-amd64.zip.sha256 2>/dev/null || echo "")
|
|
||||||
IDE_WIN_AMD64=$(cat dist/core-ide-windows-amd64.zip.sha256 2>/dev/null || echo "")
|
|
||||||
|
|
||||||
# Clone scoop bucket
|
|
||||||
gh repo clone host-uk/scoop-bucket /tmp/scoop -- --depth=1
|
|
||||||
cd /tmp/scoop
|
|
||||||
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/host-uk/scoop-bucket.git"
|
|
||||||
|
|
||||||
# Write core.json manifest
|
|
||||||
cat > core.json << 'MANIFEST'
|
|
||||||
{
|
|
||||||
"version": "VERSION_PLACEHOLDER",
|
|
||||||
"description": "Host UK development CLI",
|
|
||||||
"homepage": "https://github.com/host-uk/core",
|
|
||||||
"license": "EUPL-1.2",
|
|
||||||
"architecture": {
|
|
||||||
"64bit": {
|
|
||||||
"url": "URL_PLACEHOLDER",
|
|
||||||
"hash": "HASH_PLACEHOLDER",
|
|
||||||
"bin": "core.exe"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"checkver": "github",
|
|
||||||
"autoupdate": {
|
|
||||||
"architecture": {
|
|
||||||
"64bit": {
|
|
||||||
"url": "https://github.com/host-uk/core/releases/download/v$version/core-windows-amd64.zip"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MANIFEST
|
|
||||||
|
|
||||||
sed -i "s|VERSION_PLACEHOLDER|${MANIFEST_VERSION}|g" core.json
|
|
||||||
sed -i "s|URL_PLACEHOLDER|https://github.com/host-uk/core/releases/download/${VERSION}/core-windows-amd64.zip|g" core.json
|
|
||||||
sed -i "s|HASH_PLACEHOLDER|${WIN_AMD64}|g" core.json
|
|
||||||
sed -i 's/^ //' core.json
|
|
||||||
|
|
||||||
# Write core-ide.json manifest
|
|
||||||
if [ -n "${IDE_WIN_AMD64}" ]; then
|
|
||||||
cat > core-ide.json << 'MANIFEST'
|
|
||||||
{
|
|
||||||
"version": "VERSION_PLACEHOLDER",
|
|
||||||
"description": "Host UK desktop development environment",
|
|
||||||
"homepage": "https://github.com/host-uk/core",
|
|
||||||
"license": "EUPL-1.2",
|
|
||||||
"architecture": {
|
|
||||||
"64bit": {
|
|
||||||
"url": "URL_PLACEHOLDER",
|
|
||||||
"hash": "HASH_PLACEHOLDER",
|
|
||||||
"bin": "core-ide.exe"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"checkver": "github",
|
|
||||||
"autoupdate": {
|
|
||||||
"architecture": {
|
|
||||||
"64bit": {
|
|
||||||
"url": "https://github.com/host-uk/core/releases/download/v$version/core-ide-windows-amd64.zip"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MANIFEST
|
|
||||||
sed -i "s|VERSION_PLACEHOLDER|${MANIFEST_VERSION}|g" core-ide.json
|
|
||||||
sed -i "s|URL_PLACEHOLDER|https://github.com/host-uk/core/releases/download/${VERSION}/core-ide-windows-amd64.zip|g" core-ide.json
|
|
||||||
sed -i "s|HASH_PLACEHOLDER|${IDE_WIN_AMD64}|g" core-ide.json
|
|
||||||
sed -i 's/^ //' core-ide.json
|
|
||||||
fi
|
|
||||||
|
|
||||||
git config user.name "github-actions[bot]"
|
|
||||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git add .
|
|
||||||
git diff --cached --quiet && echo "No changes to scoop bucket" && exit 0
|
|
||||||
git commit -m "Update core to ${MANIFEST_VERSION}"
|
|
||||||
git push
|
|
||||||
|
|
@ -9,9 +9,9 @@ steps:
|
||||||
- go mod download
|
- go mod download
|
||||||
- >-
|
- >-
|
||||||
go build
|
go build
|
||||||
-ldflags "-X github.com/host-uk/core/pkg/cli.AppVersion=ci
|
-ldflags "-X forge.lthn.ai/core/cli/pkg/cli.AppVersion=ci
|
||||||
-X github.com/host-uk/core/pkg/cli.BuildCommit=${CI_COMMIT_SHA:0:7}
|
-X forge.lthn.ai/core/cli/pkg/cli.BuildCommit=${CI_COMMIT_SHA:0:7}
|
||||||
-X github.com/host-uk/core/pkg/cli.BuildDate=$(date -u +%Y%m%d)"
|
-X forge.lthn.ai/core/cli/pkg/cli.BuildDate=$(date -u +%Y%m%d)"
|
||||||
-o ./bin/core .
|
-o ./bin/core .
|
||||||
- ./bin/core --version
|
- ./bin/core --version
|
||||||
|
|
||||||
|
|
|
||||||
14
README.md
14
README.md
|
|
@ -1,14 +1,14 @@
|
||||||
# Core
|
# Core
|
||||||
|
|
||||||
[](https://codecov.io/gh/host-uk/core)
|
[](https://codecov.io/gh/host-uk/core)
|
||||||
[](https://github.com/host-uk/core/actions/workflows/coverage.yml)
|
[](https://forge.lthn.ai/core/cli/actions/workflows/coverage.yml)
|
||||||
[](https://github.com/host-uk/core/actions/workflows/codescan.yml)
|
[](https://forge.lthn.ai/core/cli/actions/workflows/codescan.yml)
|
||||||
[](https://go.dev/)
|
[](https://go.dev/)
|
||||||
[](https://opensource.org/licenses/EUPL-1.2)
|
[](https://opensource.org/licenses/EUPL-1.2)
|
||||||
|
|
||||||
Core is a Web3 Framework, written in Go using Wails.io to replace Electron and the bloat of browsers that, at their core, still live in their mum's basement.
|
Core is a Web3 Framework, written in Go using Wails.io to replace Electron and the bloat of browsers that, at their core, still live in their mum's basement.
|
||||||
|
|
||||||
- Repo: https://github.com/host-uk/core
|
- Repo: https://forge.lthn.ai/core/cli
|
||||||
|
|
||||||
## Vision
|
## Vision
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ Core is an **opinionated Web3 desktop application framework** providing:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Install Core
|
# 1. Install Core
|
||||||
go install github.com/host-uk/core/cmd/core@latest
|
go install forge.lthn.ai/core/cli/cmd/core@latest
|
||||||
|
|
||||||
# 2. Verify environment
|
# 2. Verify environment
|
||||||
core doctor
|
core doctor
|
||||||
|
|
@ -44,7 +44,7 @@ For more details, see the [User Guide](docs/user-guide.md).
|
||||||
## Framework Quick Start (Go)
|
## Framework Quick Start (Go)
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import core "github.com/host-uk/core/pkg/framework/core"
|
import core "forge.lthn.ai/core/cli/pkg/framework/core"
|
||||||
|
|
||||||
app, err := core.New(
|
app, err := core.New(
|
||||||
core.WithServiceLock(),
|
core.WithServiceLock(),
|
||||||
|
|
@ -210,7 +210,7 @@ app.RegisterService(application.NewService(coreService)) // Only Core is regist
|
||||||
**Currently exposed** (see `cmd/core-gui/public/bindings/`):
|
**Currently exposed** (see `cmd/core-gui/public/bindings/`):
|
||||||
```typescript
|
```typescript
|
||||||
// From frontend:
|
// From frontend:
|
||||||
import { ACTION, Config, Service } from './bindings/github.com/host-uk/core/pkg/core'
|
import { ACTION, Config, Service } from './bindings/forge.lthn.ai/core/cli/pkg/core'
|
||||||
|
|
||||||
ACTION(msg) // Broadcast IPC message
|
ACTION(msg) // Broadcast IPC message
|
||||||
Config() // Get config service reference
|
Config() // Get config service reference
|
||||||
|
|
@ -259,7 +259,7 @@ Sub-services are accessed via Core's **IPC/ACTION system**, not direct Wails bin
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Frontend calls Core.ACTION() with typed messages
|
// Frontend calls Core.ACTION() with typed messages
|
||||||
import { ACTION } from './bindings/github.com/host-uk/core/pkg/core'
|
import { ACTION } from './bindings/forge.lthn.ai/core/cli/pkg/core'
|
||||||
|
|
||||||
// Open a window
|
// Open a window
|
||||||
ACTION({ action: "display.open_window", name: "settings", options: { Title: "Settings", Width: 800 } })
|
ACTION({ action: "display.open_window", name: "settings", options: { Title: "Settings", Width: 800 } })
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ vars:
|
||||||
SEMVER_PRERELEASE:
|
SEMVER_PRERELEASE:
|
||||||
sh: '[ "{{.SEMVER_COMMITS}}" = "0" ] && echo "" || echo "dev.{{.SEMVER_COMMITS}}"'
|
sh: '[ "{{.SEMVER_COMMITS}}" = "0" ] && echo "" || echo "dev.{{.SEMVER_COMMITS}}"'
|
||||||
# ldflags
|
# ldflags
|
||||||
PKG: "github.com/host-uk/core/pkg/cli"
|
PKG: "forge.lthn.ai/core/cli/pkg/cli"
|
||||||
LDFLAGS_BASE: >-
|
LDFLAGS_BASE: >-
|
||||||
-X {{.PKG}}.AppVersion={{.SEMVER_VERSION}}
|
-X {{.PKG}}.AppVersion={{.SEMVER_VERSION}}
|
||||||
-X {{.PKG}}.BuildCommit={{.SEMVER_COMMIT}}
|
-X {{.PKG}}.BuildCommit={{.SEMVER_COMMIT}}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ BugSETI is a system tray application that helps developers contribute to open so
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone the repository
|
# Clone the repository
|
||||||
git clone https://github.com/host-uk/core.git
|
git clone https://forge.lthn.ai/core/cli.git
|
||||||
cd core
|
cd core
|
||||||
|
|
||||||
# Build BugSETI
|
# Build BugSETI
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ description: |
|
||||||
it pulls OSS issues from GitHub, AI prepares context,
|
it pulls OSS issues from GitHub, AI prepares context,
|
||||||
you fix bugs, and it auto-submits PRs.
|
you fix bugs, and it auto-submits PRs.
|
||||||
vendor: "Lethean"
|
vendor: "Lethean"
|
||||||
homepage: "https://github.com/host-uk/core"
|
homepage: "https://forge.lthn.ai/core/cli"
|
||||||
license: "MIT"
|
license: "MIT"
|
||||||
|
|
||||||
contents:
|
contents:
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@ require (
|
||||||
forge.lthn.ai/core/cli/internal/bugseti v0.0.0
|
forge.lthn.ai/core/cli/internal/bugseti v0.0.0
|
||||||
forge.lthn.ai/core/cli/internal/bugseti/updater v0.0.0
|
forge.lthn.ai/core/cli/internal/bugseti/updater v0.0.0
|
||||||
github.com/Snider/Borg v0.2.0
|
github.com/Snider/Borg v0.2.0
|
||||||
|
forge.lthn.ai/core/cli v0.0.0
|
||||||
|
forge.lthn.ai/core/cli/internal/bugseti v0.0.0
|
||||||
|
forge.lthn.ai/core/cli/internal/bugseti/updater v0.0.0
|
||||||
github.com/wailsapp/wails/v3 v3.0.0-alpha.64
|
github.com/wailsapp/wails/v3 v3.0.0-alpha.64
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -186,7 +186,7 @@
|
||||||
<div class="flex items-center gap-6 text-sm">
|
<div class="flex items-center gap-6 text-sm">
|
||||||
<a href="#how-it-works" class="text-lethean-400 hover:text-lethean-200 transition-colors link-underline">How it works</a>
|
<a href="#how-it-works" class="text-lethean-400 hover:text-lethean-200 transition-colors link-underline">How it works</a>
|
||||||
<a href="#ecosystem" class="text-lethean-400 hover:text-lethean-200 transition-colors link-underline">Ecosystem</a>
|
<a href="#ecosystem" class="text-lethean-400 hover:text-lethean-200 transition-colors link-underline">Ecosystem</a>
|
||||||
<a href="https://github.com/host-uk/core" target="_blank" rel="noopener" class="text-lethean-400 hover:text-lethean-200 transition-colors link-underline">GitHub</a>
|
<a href="https://forge.lthn.ai/core/cli" target="_blank" rel="noopener" class="text-lethean-400 hover:text-lethean-200 transition-colors link-underline">GitHub</a>
|
||||||
<a href="#join" class="inline-flex items-center gap-1.5 px-4 py-1.5 rounded-md bg-cyan-400/10 text-cyan-400 border border-cyan-400/20 hover:bg-cyan-400/20 hover:border-cyan-400/30 transition-all text-sm font-medium">
|
<a href="#join" class="inline-flex items-center gap-1.5 px-4 py-1.5 rounded-md bg-cyan-400/10 text-cyan-400 border border-cyan-400/20 hover:bg-cyan-400/20 hover:border-cyan-400/30 transition-all text-sm font-medium">
|
||||||
Get BugSETI
|
Get BugSETI
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -249,7 +249,7 @@
|
||||||
Download BugSETI
|
Download BugSETI
|
||||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"/></svg>
|
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"/></svg>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/host-uk/core" target="_blank" rel="noopener" class="inline-flex items-center gap-2 px-6 py-3 rounded-lg border border-lethean-600/50 text-lethean-300 font-medium text-sm hover:bg-lethean-800/50 hover:border-lethean-500/50 transition-all">
|
<a href="https://forge.lthn.ai/core/cli" target="_blank" rel="noopener" class="inline-flex items-center gap-2 px-6 py-3 rounded-lg border border-lethean-600/50 text-lethean-300 font-medium text-sm hover:bg-lethean-800/50 hover:border-lethean-500/50 transition-all">
|
||||||
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>
|
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>
|
||||||
View Source
|
View Source
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -518,13 +518,13 @@
|
||||||
|
|
||||||
<!-- Download buttons -->
|
<!-- Download buttons -->
|
||||||
<div class="flex flex-col sm:flex-row items-center justify-center gap-3 mb-12">
|
<div class="flex flex-col sm:flex-row items-center justify-center gap-3 mb-12">
|
||||||
<a href="https://github.com/host-uk/core/releases" target="_blank" rel="noopener" class="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-3.5 rounded-lg bg-lethean-800 border border-lethean-600/40 text-lethean-200 font-medium text-sm hover:bg-lethean-700 hover:border-lethean-500/50 transition-all">
|
<a href="https://forge.lthn.ai/core/cli/releases" target="_blank" rel="noopener" class="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-3.5 rounded-lg bg-lethean-800 border border-lethean-600/40 text-lethean-200 font-medium text-sm hover:bg-lethean-700 hover:border-lethean-500/50 transition-all">
|
||||||
<span class="text-lg">🐧</span> Linux
|
<span class="text-lg">🐧</span> Linux
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/host-uk/core/releases" target="_blank" rel="noopener" class="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-3.5 rounded-lg bg-lethean-800 border border-lethean-600/40 text-lethean-200 font-medium text-sm hover:bg-lethean-700 hover:border-lethean-500/50 transition-all">
|
<a href="https://forge.lthn.ai/core/cli/releases" target="_blank" rel="noopener" class="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-3.5 rounded-lg bg-lethean-800 border border-lethean-600/40 text-lethean-200 font-medium text-sm hover:bg-lethean-700 hover:border-lethean-500/50 transition-all">
|
||||||
<span class="text-lg">🍎</span> macOS
|
<span class="text-lg">🍎</span> macOS
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/host-uk/core/releases" target="_blank" rel="noopener" class="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-3.5 rounded-lg bg-lethean-800 border border-lethean-600/40 text-lethean-200 font-medium text-sm hover:bg-lethean-700 hover:border-lethean-500/50 transition-all">
|
<a href="https://forge.lthn.ai/core/cli/releases" target="_blank" rel="noopener" class="w-full sm:w-auto inline-flex items-center justify-center gap-2 px-6 py-3.5 rounded-lg bg-lethean-800 border border-lethean-600/40 text-lethean-200 font-medium text-sm hover:bg-lethean-700 hover:border-lethean-500/50 transition-all">
|
||||||
<span class="text-lg">🪟</span> Windows
|
<span class="text-lg">🪟</span> Windows
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -533,7 +533,7 @@
|
||||||
<div class="gradient-border rounded-lg overflow-hidden max-w-md mx-auto">
|
<div class="gradient-border rounded-lg overflow-hidden max-w-md mx-auto">
|
||||||
<div class="bg-lethean-900 rounded-lg px-5 py-3 font-mono text-sm text-left">
|
<div class="bg-lethean-900 rounded-lg px-5 py-3 font-mono text-sm text-left">
|
||||||
<span class="text-lethean-500"># or build from source</span><br>
|
<span class="text-lethean-500"># or build from source</span><br>
|
||||||
<span class="text-cyan-400">$</span> <span class="text-lethean-300">git clone https://github.com/host-uk/core</span><br>
|
<span class="text-cyan-400">$</span> <span class="text-lethean-300">git clone https://forge.lthn.ai/core/cli</span><br>
|
||||||
<span class="text-cyan-400">$</span> <span class="text-lethean-300">cd core && go build ./cmd/bugseti</span>
|
<span class="text-cyan-400">$</span> <span class="text-lethean-300">cd core && go build ./cmd/bugseti</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -26,8 +26,8 @@ core dev work --status
|
||||||
repos:
|
repos:
|
||||||
- name: core
|
- name: core
|
||||||
path: ./core
|
path: ./core
|
||||||
url: https://github.com/host-uk/core
|
url: https://forge.lthn.ai/core/cli
|
||||||
- name: core-php
|
- name: core-php
|
||||||
path: ./core-php
|
path: ./core-php
|
||||||
url: https://github.com/host-uk/core-php
|
url: https://forge.lthn.ai/core/cli-php
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ core go mod graph | dot -Tpng -o deps.png
|
||||||
## Output
|
## Output
|
||||||
|
|
||||||
```
|
```
|
||||||
github.com/host-uk/core github.com/stretchr/testify@v1.11.1
|
forge.lthn.ai/core/cli github.com/stretchr/testify@v1.11.1
|
||||||
github.com/stretchr/testify@v1.11.1 github.com/davecgh/go-spew@v1.1.2
|
github.com/stretchr/testify@v1.11.1 github.com/davecgh/go-spew@v1.1.2
|
||||||
github.com/stretchr/testify@v1.11.1 github.com/pmezard/go-difflib@v1.0.1
|
github.com/stretchr/testify@v1.11.1 github.com/pmezard/go-difflib@v1.0.1
|
||||||
...
|
...
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ Unified interface for Go/PHP development, multi-repo management, and deployment.
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go install github.com/host-uk/core/cmd/core@latest
|
go install forge.lthn.ai/core/cli/cmd/core@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
Verify: `core doctor`
|
Verify: `core doctor`
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ It is both. The Core Framework (`pkg/core`) is a library for building Go desktop
|
||||||
The recommended way is via Go:
|
The recommended way is via Go:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go install github.com/host-uk/core/cmd/core@latest
|
go install forge.lthn.ai/core/cli/cmd/core@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
Ensure your Go bin directory is in your PATH. See [Getting Started](getting-started.md) for more options.
|
Ensure your Go bin directory is in your PATH. See [Getting Started](getting-started.md) for more options.
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ Optional (for specific features):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Install latest release
|
# Install latest release
|
||||||
go install github.com/host-uk/core/cmd/core@latest
|
go install forge.lthn.ai/core/cli/cmd/core@latest
|
||||||
|
|
||||||
# Verify installation
|
# Verify installation
|
||||||
core doctor
|
core doctor
|
||||||
|
|
@ -39,21 +39,21 @@ export PATH="$PATH:$(go env GOPATH)/bin"
|
||||||
|
|
||||||
### Option 2: Download Binary
|
### Option 2: Download Binary
|
||||||
|
|
||||||
Download pre-built binaries from [GitHub Releases](https://github.com/host-uk/core/releases):
|
Download pre-built binaries from [GitHub Releases](https://forge.lthn.ai/core/cli/releases):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# macOS (Apple Silicon)
|
# macOS (Apple Silicon)
|
||||||
curl -Lo core https://github.com/host-uk/core/releases/latest/download/core-darwin-arm64
|
curl -Lo core https://forge.lthn.ai/core/cli/releases/latest/download/core-darwin-arm64
|
||||||
chmod +x core
|
chmod +x core
|
||||||
sudo mv core /usr/local/bin/
|
sudo mv core /usr/local/bin/
|
||||||
|
|
||||||
# macOS (Intel)
|
# macOS (Intel)
|
||||||
curl -Lo core https://github.com/host-uk/core/releases/latest/download/core-darwin-amd64
|
curl -Lo core https://forge.lthn.ai/core/cli/releases/latest/download/core-darwin-amd64
|
||||||
chmod +x core
|
chmod +x core
|
||||||
sudo mv core /usr/local/bin/
|
sudo mv core /usr/local/bin/
|
||||||
|
|
||||||
# Linux (x86_64)
|
# Linux (x86_64)
|
||||||
curl -Lo core https://github.com/host-uk/core/releases/latest/download/core-linux-amd64
|
curl -Lo core https://forge.lthn.ai/core/cli/releases/latest/download/core-linux-amd64
|
||||||
chmod +x core
|
chmod +x core
|
||||||
sudo mv core /usr/local/bin/
|
sudo mv core /usr/local/bin/
|
||||||
```
|
```
|
||||||
|
|
@ -62,7 +62,7 @@ sudo mv core /usr/local/bin/
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Clone repository
|
# Clone repository
|
||||||
git clone https://github.com/host-uk/core.git
|
git clone https://forge.lthn.ai/core/cli.git
|
||||||
cd core
|
cd core
|
||||||
|
|
||||||
# Build with Task (recommended)
|
# Build with Task (recommended)
|
||||||
|
|
@ -181,7 +181,7 @@ core doctor
|
||||||
core <command> --help
|
core <command> --help
|
||||||
|
|
||||||
# Full documentation
|
# Full documentation
|
||||||
https://github.com/host-uk/core/tree/main/docs
|
https://forge.lthn.ai/core/cli/tree/main/docs
|
||||||
```
|
```
|
||||||
|
|
||||||
## See Also
|
## See Also
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,10 @@ Core is a unified CLI for the host-uk ecosystem - build, release, and deploy Go,
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Via Go (recommended)
|
# Via Go (recommended)
|
||||||
go install github.com/host-uk/core/cmd/core@latest
|
go install forge.lthn.ai/core/cli/cmd/core@latest
|
||||||
|
|
||||||
# Or download binary from releases
|
# Or download binary from releases
|
||||||
curl -Lo core https://github.com/host-uk/core/releases/latest/download/core-$(go env GOOS)-$(go env GOARCH)
|
curl -Lo core https://forge.lthn.ai/core/cli/releases/latest/download/core-$(go env GOOS)-$(go env GOARCH)
|
||||||
chmod +x core && sudo mv core /usr/local/bin/
|
chmod +x core && sudo mv core /usr/local/bin/
|
||||||
|
|
||||||
# Verify
|
# Verify
|
||||||
|
|
|
||||||
|
|
@ -364,7 +364,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/webview"
|
"forge.lthn.ai/core/cli/pkg/webview"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
@ -424,7 +424,7 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/webview"
|
"forge.lthn.ai/core/cli/pkg/webview"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,8 @@ Here is the technical documentation for the Core framework packages.
|
||||||
* **Framework Integration**: The `Service` struct embeds `framework.ServiceRuntime`, utilizing the Actor pattern (Queries and Tasks) to allow dynamic log level adjustment at runtime without restarting the application.
|
* **Framework Integration**: The `Service` struct embeds `framework.ServiceRuntime`, utilizing the Actor pattern (Queries and Tasks) to allow dynamic log level adjustment at runtime without restarting the application.
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `github.com/host-uk/core/pkg/io`: Used by `rotation.go` to handle file operations (renaming, deleting, writing) abstractly.
|
* `forge.lthn.ai/core/cli/pkg/io`: Used by `rotation.go` to handle file operations (renaming, deleting, writing) abstractly.
|
||||||
* `github.com/host-uk/core/pkg/framework`: Used by `service.go` to hook into the application lifecycle and message bus.
|
* `forge.lthn.ai/core/cli/pkg/framework`: Used by `service.go` to hook into the application lifecycle and message bus.
|
||||||
* Standard Lib: `errors`, `fmt`, `os`, `sync`, `time`.
|
* Standard Lib: `errors`, `fmt`, `os`, `sync`, `time`.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
|
|
@ -88,8 +88,8 @@ Here is the technical documentation for the Core framework packages.
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `github.com/spf13/viper`: Core logic for map merging and unmarshalling.
|
* `github.com/spf13/viper`: Core logic for map merging and unmarshalling.
|
||||||
* `gopkg.in/yaml.v3`: For marshalling data when saving.
|
* `gopkg.in/yaml.v3`: For marshalling data when saving.
|
||||||
* `github.com/host-uk/core/pkg/io`: For reading/writing config files.
|
* `forge.lthn.ai/core/cli/pkg/io`: For reading/writing config files.
|
||||||
* `github.com/host-uk/core/pkg/framework/core`: For service integration and error handling.
|
* `forge.lthn.ai/core/cli/pkg/framework/core`: For service integration and error handling.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
* **Precedence**: Verify that Environment variables override File values.
|
* **Precedence**: Verify that Environment variables override File values.
|
||||||
|
|
@ -122,7 +122,7 @@ Here is the technical documentation for the Core framework packages.
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* Standard Lib: `io`, `io/fs`, `os`, `path/filepath`, `strings`, `time`.
|
* Standard Lib: `io`, `io/fs`, `os`, `path/filepath`, `strings`, `time`.
|
||||||
* `github.com/host-uk/core/pkg/io/local`: (Implied) The concrete implementation for OS disk access.
|
* `forge.lthn.ai/core/cli/pkg/io/local`: (Implied) The concrete implementation for OS disk access.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
* **Mock fidelity**: The `MockMedium` must behave exactly like the OS. E.g., `Rename` should fail if the source doesn't exist; `Delete` should fail if a directory is not empty.
|
* **Mock fidelity**: The `MockMedium` must behave exactly like the OS. E.g., `Rename` should fail if the source doesn't exist; `Delete` should fail if a directory is not empty.
|
||||||
|
|
@ -198,10 +198,10 @@ Here is the technical documentation for the Core framework packages.
|
||||||
4. Server validates signature against User Public Key.
|
4. Server validates signature against User Public Key.
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `github.com/host-uk/core/pkg/io`: For user database storage.
|
* `forge.lthn.ai/core/cli/pkg/io`: For user database storage.
|
||||||
* `github.com/host-uk/core/pkg/crypt/lthn`: (Implied) Specific password hashing.
|
* `forge.lthn.ai/core/cli/pkg/crypt/lthn`: (Implied) Specific password hashing.
|
||||||
* `github.com/host-uk/core/pkg/crypt/pgp`: (Implied) OpenPGP operations.
|
* `forge.lthn.ai/core/cli/pkg/crypt/pgp`: (Implied) OpenPGP operations.
|
||||||
* `github.com/host-uk/core/pkg/framework/core`: Error handling.
|
* `forge.lthn.ai/core/cli/pkg/framework/core`: Error handling.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
* **Flow Verification**: Full integration test simulating a client: Register -> Get Challenge -> Decrypt/Sign (Mock Client) -> Validate -> Get Token.
|
* **Flow Verification**: Full integration test simulating a client: Register -> Get Challenge -> Decrypt/Sign (Mock Client) -> Validate -> Get Token.
|
||||||
|
|
|
||||||
|
|
@ -60,9 +60,9 @@ The `cli` package is a comprehensive application runtime and UI framework design
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
- `github.com/spf13/cobra`: The underlying command routing engine.
|
- `github.com/spf13/cobra`: The underlying command routing engine.
|
||||||
- `github.com/host-uk/core/pkg/framework`: The dependency injection and service lifecycle container.
|
- `forge.lthn.ai/core/cli/pkg/framework`: The dependency injection and service lifecycle container.
|
||||||
- `github.com/host-uk/core/pkg/i18n`: For translation and semantic grammar generation.
|
- `forge.lthn.ai/core/cli/pkg/i18n`: For translation and semantic grammar generation.
|
||||||
- `github.com/host-uk/core/pkg/log`: For structured logging.
|
- `forge.lthn.ai/core/cli/pkg/log`: For structured logging.
|
||||||
- `golang.org/x/term`: For TTY detection.
|
- `golang.org/x/term`: For TTY detection.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
|
|
@ -162,8 +162,8 @@ The `workspace` package implements the `core.Workspace` interface, providing iso
|
||||||
- **Key Management**: Delegates actual key generation to the core's `Crypt()` service but manages the storage of the resulting keys within the workspace layout.
|
- **Key Management**: Delegates actual key generation to the core's `Crypt()` service but manages the storage of the resulting keys within the workspace layout.
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
- `github.com/host-uk/core/pkg/framework/core`: Interfaces.
|
- `forge.lthn.ai/core/cli/pkg/framework/core`: Interfaces.
|
||||||
- `github.com/host-uk/core/pkg/io`: File system abstraction (`io.Medium`).
|
- `forge.lthn.ai/core/cli/pkg/io`: File system abstraction (`io.Medium`).
|
||||||
- `crypt` service (Runtime dependency): Required for `CreateWorkspace`.
|
- `crypt` service (Runtime dependency): Required for `CreateWorkspace`.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,8 @@ type Builder interface {
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `archive/tar`, `archive/zip`, `compress/gzip`: Standard library for archiving.
|
* `archive/tar`, `archive/zip`, `compress/gzip`: Standard library for archiving.
|
||||||
* `github.com/Snider/Borg/pkg/compress`: External dependency for XZ compression support.
|
* `github.com/Snider/Borg/pkg/compress`: External dependency for XZ compression support.
|
||||||
* `github.com/host-uk/core/pkg/io`: Internal interface for filesystem abstraction.
|
* `forge.lthn.ai/core/cli/pkg/io`: Internal interface for filesystem abstraction.
|
||||||
* `github.com/host-uk/core/pkg/config`: Internal centralized configuration loading.
|
* `forge.lthn.ai/core/cli/pkg/config`: Internal centralized configuration loading.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
* **Mocking IO**: Tests must implement a mock `io.Medium` to simulate file existence (`Detect`) and write operations (`Archive`) without touching the disk.
|
* **Mocking IO**: Tests must implement a mock `io.Medium` to simulate file existence (`Detect`) and write operations (`Archive`) without touching the disk.
|
||||||
|
|
@ -158,7 +158,7 @@ type RunOptions struct {
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `os/exec`: Essential for spawning the hypervisor processes.
|
* `os/exec`: Essential for spawning the hypervisor processes.
|
||||||
* `embed`: For built-in templates.
|
* `embed`: For built-in templates.
|
||||||
* `github.com/host-uk/core/pkg/io`: Filesystem access for state and logs.
|
* `forge.lthn.ai/core/cli/pkg/io`: Filesystem access for state and logs.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
* **Process Management**: Difficult to test `Run` in standard CI. Mocking `exec.Command` or the `Hypervisor` interface is required.
|
* **Process Management**: Difficult to test `Run` in standard CI. Mocking `exec.Command` or the `Hypervisor` interface is required.
|
||||||
|
|
@ -224,7 +224,7 @@ func (r *Runner) RunParallel(ctx context.Context, specs []RunSpec) (*RunAllResul
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `os/exec`: The underlying execution engine.
|
* `os/exec`: The underlying execution engine.
|
||||||
* `github.com/host-uk/core/pkg/framework`: Creates the `ServiceRuntime` and provides the IPC/Action bus.
|
* `forge.lthn.ai/core/cli/pkg/framework`: Creates the `ServiceRuntime` and provides the IPC/Action bus.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
* **Concurrency**: The `Runner` needs tests for race conditions during parallel execution.
|
* **Concurrency**: The `Runner` needs tests for race conditions during parallel execution.
|
||||||
|
|
@ -286,7 +286,7 @@ type JobHandler interface {
|
||||||
* **Journaling**: Writes `jsonl` (JSON Lines) files partitioned by repository and date (`baseDir/owner/repo/YYYY-MM-DD.jsonl`), ensuring an append-only audit trail.
|
* **Journaling**: Writes `jsonl` (JSON Lines) files partitioned by repository and date (`baseDir/owner/repo/YYYY-MM-DD.jsonl`), ensuring an append-only audit trail.
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `github.com/host-uk/core/pkg/log`: Internal logging.
|
* `forge.lthn.ai/core/cli/pkg/log`: Internal logging.
|
||||||
* `encoding/json`: For journal serialization.
|
* `encoding/json`: For journal serialization.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ func NewService(opts ServiceOptions) func(*framework.Core) (any, error)
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
* `os/exec`: For invoking git commands.
|
* `os/exec`: For invoking git commands.
|
||||||
* `github.com/host-uk/core/pkg/framework`: For service registration and message passing types.
|
* `forge.lthn.ai/core/cli/pkg/framework`: For service registration and message passing types.
|
||||||
|
|
||||||
### Test Coverage Notes
|
### Test Coverage Notes
|
||||||
* **Mocking**: Testing requires abstracting `exec.Command`. Since this package calls `exec.CommandContext` directly, tests likely require overriding a package-level variable or using a "fake exec" pattern during test initialization.
|
* **Mocking**: Testing requires abstracting `exec.Command`. Since this package calls `exec.CommandContext` directly, tests likely require overriding a package-level variable or using a "fake exec" pattern during test initialization.
|
||||||
|
|
@ -135,7 +135,7 @@ func (repo *Repo) IsGitRepo() bool
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
* `gopkg.in/yaml.v3`: For parsing `repos.yaml`.
|
* `gopkg.in/yaml.v3`: For parsing `repos.yaml`.
|
||||||
* `github.com/host-uk/core/pkg/io`: For filesystem abstraction (`io.Medium`).
|
* `forge.lthn.ai/core/cli/pkg/io`: For filesystem abstraction (`io.Medium`).
|
||||||
|
|
||||||
### Test Coverage Notes
|
### Test Coverage Notes
|
||||||
* **Circular Dependencies**: Critical test cases must define a registry with `A->B->A` dependencies to ensure `TopologicalOrder` returns a clear error and doesn't stack overflow.
|
* **Circular Dependencies**: Critical test cases must define a registry with `A->B->A` dependencies to ensure `TopologicalOrder` returns a clear error and doesn't stack overflow.
|
||||||
|
|
@ -197,7 +197,7 @@ func (c *Client) ListUserRepos(...)
|
||||||
### Dependencies
|
### Dependencies
|
||||||
* `code.gitea.io/sdk/gitea` (for `pkg/gitea`)
|
* `code.gitea.io/sdk/gitea` (for `pkg/gitea`)
|
||||||
* `codeberg.org/mvdkleijn/forgejo-sdk` (for `pkg/forge`)
|
* `codeberg.org/mvdkleijn/forgejo-sdk` (for `pkg/forge`)
|
||||||
* `github.com/host-uk/core/pkg/config`: For persistent auth storage.
|
* `forge.lthn.ai/core/cli/pkg/config`: For persistent auth storage.
|
||||||
|
|
||||||
### Test Coverage Notes
|
### Test Coverage Notes
|
||||||
* **Draft Status**: The raw HTTP patch in `pkg/forge` needs integration testing against a real instance or a high-fidelity HTTP mock to ensure payload format matches Forgejo's API expectation.
|
* **Draft Status**: The raw HTTP patch in `pkg/forge` needs integration testing against a real instance or a high-fidelity HTTP mock to ensure payload format matches Forgejo's API expectation.
|
||||||
|
|
@ -250,8 +250,8 @@ func IncrementVersion(current string) string
|
||||||
* **SDK Generation**: Includes a specialized sub-pipeline (`RunSDK`) that handles OpenAPI diffing and client generation.
|
* **SDK Generation**: Includes a specialized sub-pipeline (`RunSDK`) that handles OpenAPI diffing and client generation.
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
* `github.com/host-uk/core/pkg/build`: For compiling artifacts.
|
* `forge.lthn.ai/core/cli/pkg/build`: For compiling artifacts.
|
||||||
* `github.com/host-uk/core/pkg/release/publishers`: Interface definitions for publishing targets.
|
* `forge.lthn.ai/core/cli/pkg/release/publishers`: Interface definitions for publishing targets.
|
||||||
* `golang.org/x/text`: For title casing in changelogs.
|
* `golang.org/x/text`: For title casing in changelogs.
|
||||||
|
|
||||||
### Test Coverage Notes
|
### Test Coverage Notes
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@ func ListAgents(cfg *config.Config) (map[string]AgentConfig, error)
|
||||||
* **Defaults Handling**: `LoadAgents` applies specific logic defaults (e.g., default queue directories, default models like "sonnet") to ensure the system works with minimal configuration.
|
* **Defaults Handling**: `LoadAgents` applies specific logic defaults (e.g., default queue directories, default models like "sonnet") to ensure the system works with minimal configuration.
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `github.com/host-uk/core/pkg/config`: For reading/writing the persistent configuration state.
|
* `forge.lthn.ai/core/cli/pkg/config`: For reading/writing the persistent configuration state.
|
||||||
* `github.com/host-uk/core/pkg/jobrunner/handlers`: To map local config structs to the runtime types used by the job dispatch system.
|
* `forge.lthn.ai/core/cli/pkg/jobrunner/handlers`: To map local config structs to the runtime types used by the job dispatch system.
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
* **Configuration Persistence**: Tests should verify that `SaveAgent` correctly updates the underlying config file and that `LoadAgents` retrieves it accurately.
|
* **Configuration Persistence**: Tests should verify that `SaveAgent` correctly updates the underlying config file and that `LoadAgents` retrieves it accurately.
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ type TaskResult struct { Changed, Failed bool; Msg, Stdout string; ... }
|
||||||
* **SSH Abstraction**: `ssh.go` wraps `golang.org/x/crypto/ssh` to handle connection pooling, key management, and `sudo` escalation (become).
|
* **SSH Abstraction**: `ssh.go` wraps `golang.org/x/crypto/ssh` to handle connection pooling, key management, and `sudo` escalation (become).
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `github.com/host-uk/core/pkg/log`: structured logging.
|
* `forge.lthn.ai/core/cli/pkg/log`: structured logging.
|
||||||
* `golang.org/x/crypto/ssh`: Underlying SSH transport.
|
* `golang.org/x/crypto/ssh`: Underlying SSH transport.
|
||||||
* `gopkg.in/yaml.v3`: YAML parsing.
|
* `gopkg.in/yaml.v3`: YAML parsing.
|
||||||
|
|
||||||
|
|
@ -129,7 +129,7 @@ Re-exports `Core`, `Option`, `Message`, `Startable`, `Stoppable`, and constructo
|
||||||
Purely structural; contains type aliases and variable assignments to expose the internal `core` package.
|
Purely structural; contains type aliases and variable assignments to expose the internal `core` package.
|
||||||
|
|
||||||
### 4. Dependencies
|
### 4. Dependencies
|
||||||
* `github.com/host-uk/core/pkg/framework/core`
|
* `forge.lthn.ai/core/cli/pkg/framework/core`
|
||||||
|
|
||||||
### 5. Test Coverage Notes
|
### 5. Test Coverage Notes
|
||||||
No logic to test directly; coverage belongs in `pkg/framework/core`.
|
No logic to test directly; coverage belongs in `pkg/framework/core`.
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,7 @@ package mypackage
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
"github.com/host-uk/core/pkg/framework"
|
"forge.lthn.ai/core/cli/pkg/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service provides mypackage functionality with Core integration.
|
// Service provides mypackage functionality with Core integration.
|
||||||
|
|
@ -120,7 +120,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/framework"
|
"forge.lthn.ai/core/cli/pkg/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Global default service
|
// Global default service
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ The `pkg/i18n` package provides internationalisation and localisation for Go CLI
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```go
|
```go
|
||||||
import "github.com/host-uk/core/pkg/i18n"
|
import "forge.lthn.ai/core/cli/pkg/i18n"
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Initialise with embedded locales
|
// Initialise with embedded locales
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
**Files:**
|
**Files:**
|
||||||
- Create: `go.work`
|
- Create: `go.work`
|
||||||
|
|
||||||
**Context:** The repo has two real modules — the root (`github.com/host-uk/core`) and core-ide (`github.com/host-uk/core/internal/core-ide`). Without a workspace, core-ide can't import `pkg/jobrunner` from the root module during local development without fragile `replace` directives. A `go.work` file makes cross-module imports resolve locally, keeps each module's `go.mod` clean, and lets CI build each variant independently.
|
**Context:** The repo has two real modules — the root (`forge.lthn.ai/core/cli`) and core-ide (`forge.lthn.ai/core/cli/internal/core-ide`). Without a workspace, core-ide can't import `pkg/jobrunner` from the root module during local development without fragile `replace` directives. A `go.work` file makes cross-module imports resolve locally, keeps each module's `go.mod` clean, and lets CI build each variant independently.
|
||||||
|
|
||||||
**Step 1: Create the workspace file**
|
**Step 1: Create the workspace file**
|
||||||
|
|
||||||
|
|
@ -580,7 +580,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/log"
|
"forge.lthn.ai/core/cli/pkg/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PollerConfig configures the job runner poller.
|
// PollerConfig configures the job runner poller.
|
||||||
|
|
@ -732,7 +732,7 @@ func (p *Poller) AddHandler(h JobHandler) {
|
||||||
_ = fmt.Sprintf // ensure fmt imported for future use
|
_ = fmt.Sprintf // ensure fmt imported for future use
|
||||||
```
|
```
|
||||||
|
|
||||||
Wait — remove that last line. The `fmt` import is only needed if used. Let me correct: the implementation above doesn't use `fmt` directly, so remove it from imports. The `log` package import path is `github.com/host-uk/core/pkg/log`.
|
Wait — remove that last line. The `fmt` import is only needed if used. Let me correct: the implementation above doesn't use `fmt` directly, so remove it from imports. The `log` package import path is `forge.lthn.ai/core/cli/pkg/log`.
|
||||||
|
|
||||||
**Step 4: Run tests**
|
**Step 4: Run tests**
|
||||||
|
|
||||||
|
|
@ -755,7 +755,7 @@ git commit -m "feat(jobrunner): add Poller with multi-source dispatch and journa
|
||||||
- Create: `pkg/jobrunner/github/signals.go`
|
- Create: `pkg/jobrunner/github/signals.go`
|
||||||
- Test: `pkg/jobrunner/github/source_test.go`
|
- Test: `pkg/jobrunner/github/source_test.go`
|
||||||
|
|
||||||
**Context:** This package lives in the root go.mod (`github.com/host-uk/core`), NOT in the core-ide module. It uses `oauth2` and the GitHub REST API (same pattern as `internal/cmd/updater/github.go`). Uses conditional requests (ETag/If-None-Match) to conserve rate limit.
|
**Context:** This package lives in the root go.mod (`forge.lthn.ai/core/cli`), NOT in the core-ide module. It uses `oauth2` and the GitHub REST API (same pattern as `internal/cmd/updater/github.go`). Uses conditional requests (ETag/If-None-Match) to conserve rate limit.
|
||||||
|
|
||||||
**Step 1: Write the test**
|
**Step 1: Write the test**
|
||||||
|
|
||||||
|
|
@ -769,7 +769,7 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
@ -856,7 +856,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ghIssue is the minimal structure from GitHub Issues API.
|
// ghIssue is the minimal structure from GitHub Issues API.
|
||||||
|
|
@ -985,8 +985,8 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
"github.com/host-uk/core/pkg/log"
|
"forge.lthn.ai/core/cli/pkg/log"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1176,7 +1176,7 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
@ -1259,7 +1259,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PublishDraft marks a draft PR as ready for review.
|
// PublishDraft marks a draft PR as ready for review.
|
||||||
|
|
@ -1355,7 +1355,7 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
@ -1438,7 +1438,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendFixCommand comments on a PR to request a fix.
|
// SendFixCommand comments on a PR to request a fix.
|
||||||
|
|
@ -1559,7 +1559,7 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
)
|
)
|
||||||
|
|
||||||
type EnableAutoMerge struct{}
|
type EnableAutoMerge struct{}
|
||||||
|
|
@ -1657,7 +1657,7 @@ import (
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
@ -1758,7 +1758,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/jobrunner"
|
"forge.lthn.ai/core/cli/pkg/jobrunner"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ResolveThreads resolves all unresolved review threads on a PR.
|
// ResolveThreads resolves all unresolved review threads on a PR.
|
||||||
|
|
@ -1918,16 +1918,16 @@ git commit -m "feat(jobrunner): add resolve_threads handler with GraphQL"
|
||||||
|
|
||||||
**Context:** core-ide currently always creates a Wails app. We need to branch: headless starts the poller + MCP bridge directly; desktop mode keeps the existing Wails app with poller as an optional service.
|
**Context:** core-ide currently always creates a Wails app. We need to branch: headless starts the poller + MCP bridge directly; desktop mode keeps the existing Wails app with poller as an optional service.
|
||||||
|
|
||||||
Note: core-ide has its own `go.mod` (`github.com/host-uk/core/internal/core-ide`). The jobrunner package lives in the root module. We need to add the root module as a dependency of core-ide, OR move the handler wiring into the root module. **Simplest approach:** core-ide imports `github.com/host-uk/core/pkg/jobrunner` — this requires adding the root module as a dependency in core-ide's go.mod.
|
Note: core-ide has its own `go.mod` (`forge.lthn.ai/core/cli/internal/core-ide`). The jobrunner package lives in the root module. We need to add the root module as a dependency of core-ide, OR move the handler wiring into the root module. **Simplest approach:** core-ide imports `forge.lthn.ai/core/cli/pkg/jobrunner` — this requires adding the root module as a dependency in core-ide's go.mod.
|
||||||
|
|
||||||
**Step 1: Update core-ide go.mod**
|
**Step 1: Update core-ide go.mod**
|
||||||
|
|
||||||
Run: `cd /Users/snider/Code/host-uk/core/internal/core-ide && go get github.com/host-uk/core/pkg/jobrunner`
|
Run: `cd /Users/snider/Code/host-uk/core/internal/core-ide && go get forge.lthn.ai/core/cli/pkg/jobrunner`
|
||||||
|
|
||||||
If this fails because the package isn't published yet, use a `replace` directive temporarily:
|
If this fails because the package isn't published yet, use a `replace` directive temporarily:
|
||||||
|
|
||||||
```
|
```
|
||||||
replace github.com/host-uk/core => ../..
|
replace forge.lthn.ai/core/cli => ../..
|
||||||
```
|
```
|
||||||
|
|
||||||
Then `go mod tidy`.
|
Then `go mod tidy`.
|
||||||
|
|
@ -2020,7 +2020,7 @@ git commit -m "feat(core-ide): register job handlers as MCP tools"
|
||||||
```go
|
```go
|
||||||
// In startHeadless(), before starting poller:
|
// In startHeadless(), before starting poller:
|
||||||
updaterSvc, err := updater.NewUpdateService(updater.UpdateServiceConfig{
|
updaterSvc, err := updater.NewUpdateService(updater.UpdateServiceConfig{
|
||||||
RepoURL: "https://github.com/host-uk/core",
|
RepoURL: "https://forge.lthn.ai/core/cli",
|
||||||
Channel: "alpha",
|
Channel: "alpha",
|
||||||
CheckOnStartup: updater.CheckAndUpdateOnStartup,
|
CheckOnStartup: updater.CheckAndUpdateOnStartup,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
ragcmd "github.com/host-uk/core/internal/cmd/rag"
|
ragcmd "forge.lthn.ai/core/cli/internal/cmd/rag"
|
||||||
"github.com/host-uk/core/pkg/rag"
|
"forge.lthn.ai/core/cli/pkg/rag"
|
||||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -368,7 +368,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/ai"
|
"forge.lthn.ai/core/cli/pkg/ai"
|
||||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -608,9 +608,9 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/cli"
|
"forge.lthn.ai/core/cli/pkg/cli"
|
||||||
"github.com/host-uk/core/pkg/i18n"
|
"forge.lthn.ai/core/cli/pkg/i18n"
|
||||||
"github.com/host-uk/core/pkg/mcp"
|
"forge.lthn.ai/core/cli/pkg/mcp"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
@ -695,7 +695,7 @@ Modify `internal/variants/full.go` to add:
|
||||||
```go
|
```go
|
||||||
import (
|
import (
|
||||||
// ... existing imports ...
|
// ... existing imports ...
|
||||||
_ "github.com/host-uk/core/internal/cmd/mcpcmd"
|
_ "forge.lthn.ai/core/cli/internal/cmd/mcpcmd"
|
||||||
)
|
)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ export PATH="$PATH:$(go env GOPATH)/bin"
|
||||||
source ~/.bashrc # or ~/.zshrc
|
source ~/.bashrc # or ~/.zshrc
|
||||||
```
|
```
|
||||||
|
|
||||||
### "go: module github.com/host-uk/core: no matching versions"
|
### "go: module forge.lthn.ai/core/cli: no matching versions"
|
||||||
|
|
||||||
**Cause:** Go module proxy hasn't cached the latest version yet.
|
**Cause:** Go module proxy hasn't cached the latest version yet.
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ source ~/.bashrc # or ~/.zshrc
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Bypass proxy
|
# Bypass proxy
|
||||||
GOPROXY=direct go install github.com/host-uk/core/cmd/core@latest
|
GOPROXY=direct go install forge.lthn.ai/core/cli/cmd/core@latest
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -340,7 +340,7 @@ This verifies all required tools are installed and configured.
|
||||||
|
|
||||||
If you've found a bug:
|
If you've found a bug:
|
||||||
|
|
||||||
1. Check existing issues: https://github.com/host-uk/core/issues
|
1. Check existing issues: https://forge.lthn.ai/core/cli/issues
|
||||||
2. Create a new issue with:
|
2. Create a new issue with:
|
||||||
- Core version (`core --version`)
|
- Core version (`core --version`)
|
||||||
- OS and architecture (`go env GOOS GOARCH`)
|
- OS and architecture (`go env GOOS GOARCH`)
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ jobs:
|
||||||
go-version: '1.23'
|
go-version: '1.23'
|
||||||
|
|
||||||
- name: Install Core
|
- name: Install Core
|
||||||
run: go install github.com/host-uk/core/cmd/core@latest
|
run: go install forge.lthn.ai/core/cli/cmd/core@latest
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: core build --ci
|
run: core build --ci
|
||||||
|
|
|
||||||
3
go.work
3
go.work
|
|
@ -2,9 +2,6 @@ go 1.25.5
|
||||||
|
|
||||||
use (
|
use (
|
||||||
.
|
.
|
||||||
./cmd/bugseti
|
|
||||||
./cmd/core-app
|
|
||||||
./cmd/core-ide
|
|
||||||
./internal/bugseti
|
./internal/bugseti
|
||||||
./internal/bugseti/updater
|
./internal/bugseti/updater
|
||||||
./internal/core-ide
|
./internal/core-ide
|
||||||
|
|
|
||||||
|
|
@ -313,7 +313,7 @@ func (s *SubmitService) generatePRBody(issue *Issue) string {
|
||||||
body.WriteString("<!-- Describe how you tested your changes -->\n\n")
|
body.WriteString("<!-- Describe how you tested your changes -->\n\n")
|
||||||
|
|
||||||
body.WriteString("---\n\n")
|
body.WriteString("---\n\n")
|
||||||
body.WriteString("*Submitted via [BugSETI](https://bugseti.app) - Distributed Bug Fixing*\n")
|
body.WriteString("*Submitted via [BugSETI](https://forge.lthn.ai/core/cli) - Distributed Bug Fixing*\n")
|
||||||
|
|
||||||
return body.String()
|
return body.String()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Core IDE Job Runner (Headless Mode)
|
Description=Core IDE Job Runner (Headless Mode)
|
||||||
Documentation=https://github.com/host-uk/core
|
Documentation=https://forge.lthn.ai/core/cli
|
||||||
After=network-online.target
|
After=network-online.target
|
||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[Unit]
|
[Unit]
|
||||||
Description=Core IDE Job Runner (User Mode)
|
Description=Core IDE Job Runner (User Mode)
|
||||||
Documentation=https://github.com/host-uk/core
|
Documentation=https://forge.lthn.ai/core/cli
|
||||||
After=network-online.target
|
After=network-online.target
|
||||||
Wants=network-online.target
|
Wants=network-online.target
|
||||||
|
|
||||||
|
|
|
||||||
11
internal/core-ide/frontend/package-lock.json
generated
11
internal/core-ide/frontend/package-lock.json
generated
|
|
@ -5495,17 +5495,6 @@
|
||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/hono": {
|
|
||||||
"version": "4.11.7",
|
|
||||||
"resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz",
|
|
||||||
"integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.9.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/hosted-git-info": {
|
"node_modules/hosted-git-info": {
|
||||||
"version": "9.0.2",
|
"version": "9.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,8 @@ require (
|
||||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
github.com/gorilla/websocket v1.5.3
|
github.com/gorilla/websocket v1.5.3
|
||||||
|
forge.lthn.ai/core/cli v0.0.0
|
||||||
|
forge.lthn.ai/core/cli-gui v0.0.0
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||||
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
|
github.com/jchv/go-winloader v0.0.0-20250406163304-c1995be93bd1 // indirect
|
||||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ site_name: Core Framework
|
||||||
site_url: https://core.help
|
site_url: https://core.help
|
||||||
site_description: 'A Web3 Framework for building Go desktop applications with Wails v3'
|
site_description: 'A Web3 Framework for building Go desktop applications with Wails v3'
|
||||||
site_author: 'Snider'
|
site_author: 'Snider'
|
||||||
repo_url: 'https://github.com/host-uk/core'
|
repo_url: 'https://forge.lthn.ai/core/cli'
|
||||||
repo_name: 'host-uk/core'
|
repo_name: 'host-uk/core'
|
||||||
|
|
||||||
theme:
|
theme:
|
||||||
|
|
|
||||||
560
pkg/devkit/devkit.go
Normal file
560
pkg/devkit/devkit.go
Normal file
|
|
@ -0,0 +1,560 @@
|
||||||
|
// Package devkit provides a developer toolkit for common automation commands.
|
||||||
|
// Designed by Gemini 3 Pro (Hypnos) + Claude Opus (Charon), signed LEK-1 | lthn.ai | EUPL-1.2
|
||||||
|
package devkit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// --- Code Quality ---
|
||||||
|
|
||||||
|
// Finding represents a single issue found by a linting tool.
|
||||||
|
type Finding struct {
|
||||||
|
File string
|
||||||
|
Line int
|
||||||
|
Message string
|
||||||
|
Tool string
|
||||||
|
}
|
||||||
|
|
||||||
|
// CoverageReport holds the test coverage percentage for a package.
|
||||||
|
type CoverageReport struct {
|
||||||
|
Package string
|
||||||
|
Percentage float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaceCondition represents a data race detected by the Go race detector.
|
||||||
|
type RaceCondition struct {
|
||||||
|
File string
|
||||||
|
Line int
|
||||||
|
Desc string
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO represents a tracked code comment like TODO, FIXME, or HACK.
|
||||||
|
type TODO struct {
|
||||||
|
File string
|
||||||
|
Line int
|
||||||
|
Type string
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Security ---
|
||||||
|
|
||||||
|
// Vulnerability represents a dependency vulnerability.
|
||||||
|
type Vulnerability struct {
|
||||||
|
ID string
|
||||||
|
Package string
|
||||||
|
Version string
|
||||||
|
Description string
|
||||||
|
}
|
||||||
|
|
||||||
|
// SecretLeak represents a potential secret found in the codebase.
|
||||||
|
type SecretLeak struct {
|
||||||
|
File string
|
||||||
|
Line int
|
||||||
|
RuleID string
|
||||||
|
Match string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PermIssue represents a file permission issue.
|
||||||
|
type PermIssue struct {
|
||||||
|
File string
|
||||||
|
Permission string
|
||||||
|
Issue string
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Git Operations ---
|
||||||
|
|
||||||
|
// DiffSummary provides a summary of changes.
|
||||||
|
type DiffSummary struct {
|
||||||
|
FilesChanged int
|
||||||
|
Insertions int
|
||||||
|
Deletions int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commit represents a single git commit.
|
||||||
|
type Commit struct {
|
||||||
|
Hash string
|
||||||
|
Author string
|
||||||
|
Date time.Time
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Build & Dependencies ---
|
||||||
|
|
||||||
|
// BuildResult holds the outcome of a single build target.
|
||||||
|
type BuildResult struct {
|
||||||
|
Target string
|
||||||
|
Path string
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
// Graph represents a dependency graph.
|
||||||
|
type Graph struct {
|
||||||
|
Nodes []string
|
||||||
|
Edges map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Metrics ---
|
||||||
|
|
||||||
|
// ComplexFunc represents a function with its cyclomatic complexity score.
|
||||||
|
type ComplexFunc struct {
|
||||||
|
Package string
|
||||||
|
FuncName string
|
||||||
|
File string
|
||||||
|
Line int
|
||||||
|
Score int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toolkit wraps common dev automation commands into structured Go APIs.
|
||||||
|
type Toolkit struct {
|
||||||
|
Dir string // Working directory for commands
|
||||||
|
}
|
||||||
|
|
||||||
|
// New creates a Toolkit rooted at the given directory.
|
||||||
|
func New(dir string) *Toolkit {
|
||||||
|
return &Toolkit{Dir: dir}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run executes a command and captures stdout, stderr, and exit code.
|
||||||
|
func (t *Toolkit) Run(name string, args ...string) (stdout, stderr string, exitCode int, err error) {
|
||||||
|
cmd := exec.Command(name, args...)
|
||||||
|
cmd.Dir = t.Dir
|
||||||
|
var stdoutBuf, stderrBuf bytes.Buffer
|
||||||
|
cmd.Stdout = &stdoutBuf
|
||||||
|
cmd.Stderr = &stderrBuf
|
||||||
|
|
||||||
|
err = cmd.Run()
|
||||||
|
stdout = stdoutBuf.String()
|
||||||
|
stderr = stderrBuf.String()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if exitErr, ok := err.(*exec.ExitError); ok {
|
||||||
|
exitCode = exitErr.ExitCode()
|
||||||
|
} else {
|
||||||
|
exitCode = -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindTODOs greps for TODO/FIXME/HACK comments within a directory.
|
||||||
|
func (t *Toolkit) FindTODOs(dir string) ([]TODO, error) {
|
||||||
|
pattern := `\b(TODO|FIXME|HACK)\b(\(.*\))?:`
|
||||||
|
stdout, stderr, exitCode, err := t.Run("git", "grep", "--line-number", "-E", pattern, "--", dir)
|
||||||
|
|
||||||
|
if exitCode == 1 && stdout == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil && exitCode != 1 {
|
||||||
|
return nil, fmt.Errorf("git grep failed (exit %d): %s\n%s", exitCode, err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var todos []TODO
|
||||||
|
re := regexp.MustCompile(pattern)
|
||||||
|
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(stdout), "\n") {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(line, ":", 3)
|
||||||
|
if len(parts) < 3 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lineNum, _ := strconv.Atoi(parts[1])
|
||||||
|
match := re.FindStringSubmatch(parts[2])
|
||||||
|
todoType := ""
|
||||||
|
if len(match) > 1 {
|
||||||
|
todoType = match[1]
|
||||||
|
}
|
||||||
|
msg := strings.TrimSpace(re.Split(parts[2], 2)[1])
|
||||||
|
|
||||||
|
todos = append(todos, TODO{
|
||||||
|
File: parts[0],
|
||||||
|
Line: lineNum,
|
||||||
|
Type: todoType,
|
||||||
|
Message: msg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return todos, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuditDeps runs govulncheck to find dependency vulnerabilities.
|
||||||
|
func (t *Toolkit) AuditDeps() ([]Vulnerability, error) {
|
||||||
|
stdout, stderr, exitCode, err := t.Run("govulncheck", "./...")
|
||||||
|
if err != nil && exitCode != 0 && !strings.Contains(stdout, "Vulnerability") {
|
||||||
|
return nil, fmt.Errorf("govulncheck failed (exit %d): %s\n%s", exitCode, err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var vulns []Vulnerability
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||||
|
var cur Vulnerability
|
||||||
|
inBlock := false
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, "Vulnerability #") {
|
||||||
|
if cur.ID != "" {
|
||||||
|
vulns = append(vulns, cur)
|
||||||
|
}
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
cur = Vulnerability{}
|
||||||
|
if len(fields) > 1 {
|
||||||
|
cur.ID = fields[1]
|
||||||
|
}
|
||||||
|
inBlock = true
|
||||||
|
} else if inBlock {
|
||||||
|
switch {
|
||||||
|
case strings.Contains(line, "Package:"):
|
||||||
|
cur.Package = strings.TrimSpace(strings.SplitN(line, ":", 2)[1])
|
||||||
|
case strings.Contains(line, "Found in version:"):
|
||||||
|
cur.Version = strings.TrimSpace(strings.SplitN(line, ":", 2)[1])
|
||||||
|
case line == "":
|
||||||
|
if cur.ID != "" {
|
||||||
|
vulns = append(vulns, cur)
|
||||||
|
cur = Vulnerability{}
|
||||||
|
}
|
||||||
|
inBlock = false
|
||||||
|
default:
|
||||||
|
if !strings.HasPrefix(line, " ") && cur.Description == "" {
|
||||||
|
cur.Description = strings.TrimSpace(line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cur.ID != "" {
|
||||||
|
vulns = append(vulns, cur)
|
||||||
|
}
|
||||||
|
return vulns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiffStat returns a summary of uncommitted changes.
|
||||||
|
func (t *Toolkit) DiffStat() (DiffSummary, error) {
|
||||||
|
stdout, stderr, exitCode, err := t.Run("git", "diff", "--stat")
|
||||||
|
if err != nil && exitCode != 0 {
|
||||||
|
return DiffSummary{}, fmt.Errorf("git diff failed (exit %d): %s\n%s", exitCode, err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var s DiffSummary
|
||||||
|
lines := strings.Split(strings.TrimSpace(stdout), "\n")
|
||||||
|
if len(lines) == 0 || lines[0] == "" {
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
last := lines[len(lines)-1]
|
||||||
|
for _, part := range strings.Split(last, ",") {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
fields := strings.Fields(part)
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val, _ := strconv.Atoi(fields[0])
|
||||||
|
switch {
|
||||||
|
case strings.Contains(part, "file"):
|
||||||
|
s.FilesChanged = val
|
||||||
|
case strings.Contains(part, "insertion"):
|
||||||
|
s.Insertions = val
|
||||||
|
case strings.Contains(part, "deletion"):
|
||||||
|
s.Deletions = val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UncommittedFiles returns paths of files with uncommitted changes.
|
||||||
|
func (t *Toolkit) UncommittedFiles() ([]string, error) {
|
||||||
|
stdout, stderr, exitCode, err := t.Run("git", "status", "--porcelain")
|
||||||
|
if err != nil && exitCode != 0 {
|
||||||
|
return nil, fmt.Errorf("git status failed: %s\n%s", err, stderr)
|
||||||
|
}
|
||||||
|
var files []string
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(stdout), "\n") {
|
||||||
|
if len(line) > 3 {
|
||||||
|
files = append(files, strings.TrimSpace(line[3:]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lint runs go vet on the given package pattern.
|
||||||
|
func (t *Toolkit) Lint(pkg string) ([]Finding, error) {
|
||||||
|
_, stderr, exitCode, err := t.Run("go", "vet", pkg)
|
||||||
|
if exitCode == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil && exitCode != 2 {
|
||||||
|
return nil, fmt.Errorf("go vet failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var findings []Finding
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(stderr), "\n") {
|
||||||
|
if line == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(line, ":", 4)
|
||||||
|
if len(parts) < 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lineNum, _ := strconv.Atoi(parts[1])
|
||||||
|
findings = append(findings, Finding{
|
||||||
|
File: parts[0],
|
||||||
|
Line: lineNum,
|
||||||
|
Message: strings.TrimSpace(parts[3]),
|
||||||
|
Tool: "go vet",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return findings, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ScanSecrets runs gitleaks to find potential secret leaks.
|
||||||
|
func (t *Toolkit) ScanSecrets(dir string) ([]SecretLeak, error) {
|
||||||
|
stdout, _, exitCode, err := t.Run("gitleaks", "detect", "--source", dir, "--report-format", "csv", "--no-git")
|
||||||
|
if exitCode == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
if err != nil && exitCode != 1 {
|
||||||
|
return nil, fmt.Errorf("gitleaks failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var leaks []SecretLeak
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(stdout), "\n") {
|
||||||
|
if line == "" || strings.HasPrefix(line, "RuleID") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(line, ",", 4)
|
||||||
|
if len(parts) < 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lineNum, _ := strconv.Atoi(parts[2])
|
||||||
|
leaks = append(leaks, SecretLeak{
|
||||||
|
RuleID: parts[0],
|
||||||
|
File: parts[1],
|
||||||
|
Line: lineNum,
|
||||||
|
Match: parts[3],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return leaks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ModTidy runs go mod tidy.
|
||||||
|
func (t *Toolkit) ModTidy() error {
|
||||||
|
_, stderr, exitCode, err := t.Run("go", "mod", "tidy")
|
||||||
|
if err != nil && exitCode != 0 {
|
||||||
|
return fmt.Errorf("go mod tidy failed: %s", stderr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build compiles the given targets.
|
||||||
|
func (t *Toolkit) Build(targets ...string) ([]BuildResult, error) {
|
||||||
|
var results []BuildResult
|
||||||
|
for _, target := range targets {
|
||||||
|
_, stderr, _, err := t.Run("go", "build", "-o", "/dev/null", target)
|
||||||
|
r := BuildResult{Target: target}
|
||||||
|
if err != nil {
|
||||||
|
r.Error = fmt.Errorf("%s", strings.TrimSpace(stderr))
|
||||||
|
}
|
||||||
|
results = append(results, r)
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestCount returns the number of test functions in a package.
|
||||||
|
func (t *Toolkit) TestCount(pkg string) (int, error) {
|
||||||
|
stdout, stderr, exitCode, err := t.Run("go", "test", "-list", ".*", pkg)
|
||||||
|
if err != nil && exitCode != 0 {
|
||||||
|
return 0, fmt.Errorf("go test -list failed: %s\n%s", err, stderr)
|
||||||
|
}
|
||||||
|
count := 0
|
||||||
|
for _, line := range strings.Split(strings.TrimSpace(stdout), "\n") {
|
||||||
|
if strings.HasPrefix(line, "Test") || strings.HasPrefix(line, "Benchmark") {
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Coverage runs go test -cover and parses per-package coverage percentages.
|
||||||
|
func (t *Toolkit) Coverage(pkg string) ([]CoverageReport, error) {
|
||||||
|
if pkg == "" {
|
||||||
|
pkg = "./..."
|
||||||
|
}
|
||||||
|
stdout, stderr, exitCode, err := t.Run("go", "test", "-cover", pkg)
|
||||||
|
if err != nil && exitCode != 0 && !strings.Contains(stdout, "coverage:") {
|
||||||
|
return nil, fmt.Errorf("go test -cover failed (exit %d): %s\n%s", exitCode, err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var reports []CoverageReport
|
||||||
|
re := regexp.MustCompile(`ok\s+(\S+)\s+.*coverage:\s+([\d.]+)%`)
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
matches := re.FindStringSubmatch(scanner.Text())
|
||||||
|
if len(matches) == 3 {
|
||||||
|
pct, _ := strconv.ParseFloat(matches[2], 64)
|
||||||
|
reports = append(reports, CoverageReport{
|
||||||
|
Package: matches[1],
|
||||||
|
Percentage: pct,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reports, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RaceDetect runs go test -race and parses data race warnings.
|
||||||
|
func (t *Toolkit) RaceDetect(pkg string) ([]RaceCondition, error) {
|
||||||
|
if pkg == "" {
|
||||||
|
pkg = "./..."
|
||||||
|
}
|
||||||
|
_, stderr, _, err := t.Run("go", "test", "-race", pkg)
|
||||||
|
if err != nil && !strings.Contains(stderr, "WARNING: DATA RACE") {
|
||||||
|
return nil, fmt.Errorf("go test -race failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var races []RaceCondition
|
||||||
|
lines := strings.Split(stderr, "\n")
|
||||||
|
reFile := regexp.MustCompile(`\s+(.*\.go):(\d+)`)
|
||||||
|
|
||||||
|
for i, line := range lines {
|
||||||
|
if strings.Contains(line, "WARNING: DATA RACE") {
|
||||||
|
rc := RaceCondition{Desc: "Data race detected"}
|
||||||
|
for j := i + 1; j < len(lines) && j < i+15; j++ {
|
||||||
|
if match := reFile.FindStringSubmatch(lines[j]); len(match) == 3 {
|
||||||
|
rc.File = strings.TrimSpace(match[1])
|
||||||
|
rc.Line, _ = strconv.Atoi(match[2])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
races = append(races, rc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return races, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complexity runs gocyclo and returns functions exceeding the threshold.
|
||||||
|
func (t *Toolkit) Complexity(threshold int) ([]ComplexFunc, error) {
|
||||||
|
stdout, stderr, exitCode, err := t.Run("gocyclo", "-over", strconv.Itoa(threshold), ".")
|
||||||
|
if err != nil && exitCode == -1 {
|
||||||
|
return nil, fmt.Errorf("gocyclo not available: %s\n%s", err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var funcs []ComplexFunc
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
fields := strings.Fields(scanner.Text())
|
||||||
|
if len(fields) < 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
score, _ := strconv.Atoi(fields[0])
|
||||||
|
fileParts := strings.Split(fields[3], ":")
|
||||||
|
line := 0
|
||||||
|
if len(fileParts) > 1 {
|
||||||
|
line, _ = strconv.Atoi(fileParts[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
funcs = append(funcs, ComplexFunc{
|
||||||
|
Score: score,
|
||||||
|
Package: fields[1],
|
||||||
|
FuncName: fields[2],
|
||||||
|
File: fileParts[0],
|
||||||
|
Line: line,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return funcs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DepGraph runs go mod graph and builds a dependency graph.
|
||||||
|
func (t *Toolkit) DepGraph(pkg string) (*Graph, error) {
|
||||||
|
stdout, stderr, exitCode, err := t.Run("go", "mod", "graph")
|
||||||
|
if err != nil && exitCode != 0 {
|
||||||
|
return nil, fmt.Errorf("go mod graph failed (exit %d): %s\n%s", exitCode, err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
graph := &Graph{Edges: make(map[string][]string)}
|
||||||
|
nodes := make(map[string]struct{})
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
parts := strings.Fields(scanner.Text())
|
||||||
|
if len(parts) >= 2 {
|
||||||
|
src, dst := parts[0], parts[1]
|
||||||
|
graph.Edges[src] = append(graph.Edges[src], dst)
|
||||||
|
nodes[src] = struct{}{}
|
||||||
|
nodes[dst] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for node := range nodes {
|
||||||
|
graph.Nodes = append(graph.Nodes, node)
|
||||||
|
}
|
||||||
|
return graph, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitLog returns the last n commits from git history.
|
||||||
|
func (t *Toolkit) GitLog(n int) ([]Commit, error) {
|
||||||
|
stdout, stderr, exitCode, err := t.Run("git", "log", fmt.Sprintf("-n%d", n), "--format=%H|%an|%aI|%s")
|
||||||
|
if err != nil && exitCode != 0 {
|
||||||
|
return nil, fmt.Errorf("git log failed (exit %d): %s\n%s", exitCode, err, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
var commits []Commit
|
||||||
|
scanner := bufio.NewScanner(strings.NewReader(stdout))
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
parts := strings.SplitN(scanner.Text(), "|", 4)
|
||||||
|
if len(parts) < 4 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
date, _ := time.Parse(time.RFC3339, parts[2])
|
||||||
|
commits = append(commits, Commit{
|
||||||
|
Hash: parts[0],
|
||||||
|
Author: parts[1],
|
||||||
|
Date: date,
|
||||||
|
Message: parts[3],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return commits, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPerms walks a directory and flags files with overly permissive modes.
|
||||||
|
func (t *Toolkit) CheckPerms(dir string) ([]PermIssue, error) {
|
||||||
|
var issues []PermIssue
|
||||||
|
err := filepath.Walk(filepath.Join(t.Dir, dir), func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mode := info.Mode().Perm()
|
||||||
|
if mode&0o002 != 0 {
|
||||||
|
issues = append(issues, PermIssue{
|
||||||
|
File: path,
|
||||||
|
Permission: fmt.Sprintf("%04o", mode),
|
||||||
|
Issue: "World-writable",
|
||||||
|
})
|
||||||
|
} else if mode&0o020 != 0 && mode&0o002 != 0 {
|
||||||
|
issues = append(issues, PermIssue{
|
||||||
|
File: path,
|
||||||
|
Permission: fmt.Sprintf("%04o", mode),
|
||||||
|
Issue: "Group and world-writable",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("walk failed: %w", err)
|
||||||
|
}
|
||||||
|
return issues, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LEK-1 | lthn.ai | EUPL-1.2
|
||||||
270
pkg/devkit/devkit_test.go
Normal file
270
pkg/devkit/devkit_test.go
Normal file
|
|
@ -0,0 +1,270 @@
|
||||||
|
// Designed by Gemini 3 Pro (Hypnos) + Claude Opus (Charon), signed LEK-1 | lthn.ai | EUPL-1.2
|
||||||
|
package devkit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setupMockCmd creates a shell script in a temp dir that echoes predetermined
|
||||||
|
// content, and prepends that dir to PATH so Run() picks it up.
|
||||||
|
func setupMockCmd(t *testing.T, name, content string) {
|
||||||
|
t.Helper()
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
scriptPath := filepath.Join(tmpDir, name)
|
||||||
|
|
||||||
|
script := fmt.Sprintf("#!/bin/sh\ncat <<'MOCK_EOF'\n%s\nMOCK_EOF\n", content)
|
||||||
|
if err := os.WriteFile(scriptPath, []byte(script), 0755); err != nil {
|
||||||
|
t.Fatalf("failed to write mock command %s: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
oldPath := os.Getenv("PATH")
|
||||||
|
t.Setenv("PATH", tmpDir+string(os.PathListSeparator)+oldPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// setupMockCmdExit creates a mock that echoes to stdout/stderr and exits with a code.
|
||||||
|
func setupMockCmdExit(t *testing.T, name, stdout, stderr string, exitCode int) {
|
||||||
|
t.Helper()
|
||||||
|
tmpDir := t.TempDir()
|
||||||
|
scriptPath := filepath.Join(tmpDir, name)
|
||||||
|
|
||||||
|
script := fmt.Sprintf("#!/bin/sh\ncat <<'MOCK_EOF'\n%s\nMOCK_EOF\ncat <<'MOCK_ERR' >&2\n%s\nMOCK_ERR\nexit %d\n", stdout, stderr, exitCode)
|
||||||
|
if err := os.WriteFile(scriptPath, []byte(script), 0755); err != nil {
|
||||||
|
t.Fatalf("failed to write mock command %s: %v", name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
oldPath := os.Getenv("PATH")
|
||||||
|
t.Setenv("PATH", tmpDir+string(os.PathListSeparator)+oldPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCoverage_Good(t *testing.T) {
|
||||||
|
output := `? example.com/skipped [no test files]
|
||||||
|
ok example.com/pkg1 0.5s coverage: 85.0% of statements
|
||||||
|
ok example.com/pkg2 0.2s coverage: 100.0% of statements`
|
||||||
|
|
||||||
|
setupMockCmd(t, "go", output)
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
reports, err := tk.Coverage("./...")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Coverage failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(reports) != 2 {
|
||||||
|
t.Fatalf("expected 2 reports, got %d", len(reports))
|
||||||
|
}
|
||||||
|
if reports[0].Package != "example.com/pkg1" || reports[0].Percentage != 85.0 {
|
||||||
|
t.Errorf("report 0: want pkg1@85%%, got %s@%.1f%%", reports[0].Package, reports[0].Percentage)
|
||||||
|
}
|
||||||
|
if reports[1].Package != "example.com/pkg2" || reports[1].Percentage != 100.0 {
|
||||||
|
t.Errorf("report 1: want pkg2@100%%, got %s@%.1f%%", reports[1].Package, reports[1].Percentage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCoverage_Bad(t *testing.T) {
|
||||||
|
// No coverage lines in output
|
||||||
|
setupMockCmd(t, "go", "FAIL\texample.com/broken [build failed]")
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
reports, err := tk.Coverage("./...")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Coverage should not error on partial output: %v", err)
|
||||||
|
}
|
||||||
|
if len(reports) != 0 {
|
||||||
|
t.Errorf("expected 0 reports from failed build, got %d", len(reports))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLog_Good(t *testing.T) {
|
||||||
|
now := time.Now().Truncate(time.Second)
|
||||||
|
nowStr := now.Format(time.RFC3339)
|
||||||
|
|
||||||
|
output := fmt.Sprintf("abc123|Alice|%s|Fix the bug\ndef456|Bob|%s|Add feature", nowStr, nowStr)
|
||||||
|
setupMockCmd(t, "git", output)
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
commits, err := tk.GitLog(2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GitLog failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(commits) != 2 {
|
||||||
|
t.Fatalf("expected 2 commits, got %d", len(commits))
|
||||||
|
}
|
||||||
|
if commits[0].Hash != "abc123" {
|
||||||
|
t.Errorf("hash: want abc123, got %s", commits[0].Hash)
|
||||||
|
}
|
||||||
|
if commits[0].Author != "Alice" {
|
||||||
|
t.Errorf("author: want Alice, got %s", commits[0].Author)
|
||||||
|
}
|
||||||
|
if commits[0].Message != "Fix the bug" {
|
||||||
|
t.Errorf("message: want 'Fix the bug', got %q", commits[0].Message)
|
||||||
|
}
|
||||||
|
if !commits[0].Date.Equal(now) {
|
||||||
|
t.Errorf("date: want %v, got %v", now, commits[0].Date)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGitLog_Bad(t *testing.T) {
|
||||||
|
// Malformed lines should be skipped
|
||||||
|
setupMockCmd(t, "git", "incomplete|line\nabc|Bob|2025-01-01T00:00:00Z|Good commit")
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
commits, err := tk.GitLog(5)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("GitLog failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(commits) != 1 {
|
||||||
|
t.Errorf("expected 1 valid commit (skip malformed), got %d", len(commits))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComplexity_Good(t *testing.T) {
|
||||||
|
output := "15 main ComplexFunc file.go:10:1\n20 pkg VeryComplex other.go:50:1"
|
||||||
|
setupMockCmd(t, "gocyclo", output)
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
funcs, err := tk.Complexity(10)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Complexity failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(funcs) != 2 {
|
||||||
|
t.Fatalf("expected 2 funcs, got %d", len(funcs))
|
||||||
|
}
|
||||||
|
if funcs[0].Score != 15 || funcs[0].FuncName != "ComplexFunc" || funcs[0].File != "file.go" || funcs[0].Line != 10 {
|
||||||
|
t.Errorf("func 0: unexpected %+v", funcs[0])
|
||||||
|
}
|
||||||
|
if funcs[1].Score != 20 || funcs[1].Package != "pkg" {
|
||||||
|
t.Errorf("func 1: unexpected %+v", funcs[1])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestComplexity_Bad(t *testing.T) {
|
||||||
|
// No functions above threshold = empty output
|
||||||
|
setupMockCmd(t, "gocyclo", "")
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
funcs, err := tk.Complexity(50)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Complexity should not error on empty output: %v", err)
|
||||||
|
}
|
||||||
|
if len(funcs) != 0 {
|
||||||
|
t.Errorf("expected 0 funcs, got %d", len(funcs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDepGraph_Good(t *testing.T) {
|
||||||
|
output := "modA@v1 modB@v2\nmodA@v1 modC@v3\nmodB@v2 modD@v1"
|
||||||
|
setupMockCmd(t, "go", output)
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
graph, err := tk.DepGraph("./...")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("DepGraph failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(graph.Nodes) != 4 {
|
||||||
|
t.Errorf("expected 4 nodes, got %d: %v", len(graph.Nodes), graph.Nodes)
|
||||||
|
}
|
||||||
|
edgesA := graph.Edges["modA@v1"]
|
||||||
|
if len(edgesA) != 2 {
|
||||||
|
t.Errorf("expected 2 edges from modA@v1, got %d", len(edgesA))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRaceDetect_Good(t *testing.T) {
|
||||||
|
// No races = clean run
|
||||||
|
setupMockCmd(t, "go", "ok\texample.com/safe\t0.1s")
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
races, err := tk.RaceDetect("./...")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RaceDetect failed on clean run: %v", err)
|
||||||
|
}
|
||||||
|
if len(races) != 0 {
|
||||||
|
t.Errorf("expected 0 races, got %d", len(races))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRaceDetect_Bad(t *testing.T) {
|
||||||
|
stderrOut := `WARNING: DATA RACE
|
||||||
|
Read at 0x00c000123456 by goroutine 7:
|
||||||
|
/home/user/project/main.go:42
|
||||||
|
Previous write at 0x00c000123456 by goroutine 6:
|
||||||
|
/home/user/project/main.go:38`
|
||||||
|
|
||||||
|
setupMockCmdExit(t, "go", "", stderrOut, 1)
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
races, err := tk.RaceDetect("./...")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("RaceDetect should parse races, not error: %v", err)
|
||||||
|
}
|
||||||
|
if len(races) != 1 {
|
||||||
|
t.Fatalf("expected 1 race, got %d", len(races))
|
||||||
|
}
|
||||||
|
if races[0].File != "/home/user/project/main.go" || races[0].Line != 42 {
|
||||||
|
t.Errorf("race: unexpected %+v", races[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDiffStat_Good(t *testing.T) {
|
||||||
|
output := ` file1.go | 10 +++++++---
|
||||||
|
file2.go | 5 +++++
|
||||||
|
2 files changed, 12 insertions(+), 3 deletions(-)`
|
||||||
|
setupMockCmd(t, "git", output)
|
||||||
|
|
||||||
|
tk := New(t.TempDir())
|
||||||
|
s, err := tk.DiffStat()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("DiffStat failed: %v", err)
|
||||||
|
}
|
||||||
|
if s.FilesChanged != 2 {
|
||||||
|
t.Errorf("files: want 2, got %d", s.FilesChanged)
|
||||||
|
}
|
||||||
|
if s.Insertions != 12 {
|
||||||
|
t.Errorf("insertions: want 12, got %d", s.Insertions)
|
||||||
|
}
|
||||||
|
if s.Deletions != 3 {
|
||||||
|
t.Errorf("deletions: want 3, got %d", s.Deletions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckPerms_Good(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
|
||||||
|
// Create a world-writable file
|
||||||
|
badFile := filepath.Join(dir, "bad.txt")
|
||||||
|
if err := os.WriteFile(badFile, []byte("test"), 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.Chmod(badFile, 0666); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
// Create a safe file
|
||||||
|
goodFile := filepath.Join(dir, "good.txt")
|
||||||
|
if err := os.WriteFile(goodFile, []byte("test"), 0644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tk := New("/")
|
||||||
|
issues, err := tk.CheckPerms(dir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("CheckPerms failed: %v", err)
|
||||||
|
}
|
||||||
|
if len(issues) != 1 {
|
||||||
|
t.Fatalf("expected 1 issue (world-writable), got %d", len(issues))
|
||||||
|
}
|
||||||
|
if issues[0].Issue != "World-writable" {
|
||||||
|
t.Errorf("issue: want 'World-writable', got %q", issues[0].Issue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNew(t *testing.T) {
|
||||||
|
tk := New("/tmp")
|
||||||
|
if tk.Dir != "/tmp" {
|
||||||
|
t.Errorf("Dir: want /tmp, got %s", tk.Dir)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LEK-1 | lthn.ai | EUPL-1.2
|
||||||
|
|
@ -1146,277 +1146,129 @@
|
||||||
"error.gh_not_found": "'gh' CLI not found. Install from https://cli.github.com/",
|
"error.gh_not_found": "'gh' CLI not found. Install from https://cli.github.com/",
|
||||||
"error.registry_not_found": "No repos.yaml found",
|
"error.registry_not_found": "No repos.yaml found",
|
||||||
"error.repo_not_found": "Repository '{{.Name}}' not found",
|
"error.repo_not_found": "Repository '{{.Name}}' not found",
|
||||||
"gram.article.definite": "the",
|
|
||||||
"gram.article.definite.feminine": "",
|
"gram": {
|
||||||
"gram.article.definite.masculine": "",
|
"verb": {
|
||||||
"gram.article.definite.neuter": "",
|
"be": { "base": "be", "past": "was", "gerund": "being" },
|
||||||
"gram.article.indefinite.default": "a",
|
"go": { "base": "go", "past": "went", "gerund": "going" },
|
||||||
"gram.article.indefinite.feminine": "",
|
"do": { "base": "do", "past": "did", "gerund": "doing" },
|
||||||
"gram.article.indefinite.masculine": "",
|
"have": { "base": "have", "past": "had", "gerund": "having" },
|
||||||
"gram.article.indefinite.neuter": "",
|
"make": { "base": "make", "past": "made", "gerund": "making" },
|
||||||
"gram.article.indefinite.vowel": "an",
|
"get": { "base": "get", "past": "got", "gerund": "getting" },
|
||||||
"gram.noun.artifact.one": "artifact",
|
"run": { "base": "run", "past": "ran", "gerund": "running" },
|
||||||
"gram.noun.artifact.other": "artifacts",
|
"write": { "base": "write", "past": "wrote", "gerund": "writing" },
|
||||||
"gram.noun.branch.gender": "",
|
"build": { "base": "build", "past": "built", "gerund": "building" },
|
||||||
"gram.noun.branch.one": "branch",
|
"send": { "base": "send", "past": "sent", "gerund": "sending" },
|
||||||
"gram.noun.branch.other": "branches",
|
"find": { "base": "find", "past": "found", "gerund": "finding" },
|
||||||
"gram.noun.category.one": "category",
|
"take": { "base": "take", "past": "took", "gerund": "taking" },
|
||||||
"gram.noun.category.other": "categories",
|
"begin": { "base": "begin", "past": "began", "gerund": "beginning" },
|
||||||
"gram.noun.change.gender": "",
|
"keep": { "base": "keep", "past": "kept", "gerund": "keeping" },
|
||||||
"gram.noun.change.one": "change",
|
"hold": { "base": "hold", "past": "held", "gerund": "holding" },
|
||||||
"gram.noun.change.other": "changes",
|
"bring": { "base": "bring", "past": "brought", "gerund": "bringing" },
|
||||||
"gram.noun.check.one": "check",
|
"think": { "base": "think", "past": "thought", "gerund": "thinking" },
|
||||||
"gram.noun.check.other": "checks",
|
"buy": { "base": "buy", "past": "bought", "gerund": "buying" },
|
||||||
"gram.noun.child.one": "child",
|
"catch": { "base": "catch", "past": "caught", "gerund": "catching" },
|
||||||
"gram.noun.child.other": "children",
|
"choose": { "base": "choose", "past": "chose", "gerund": "choosing" },
|
||||||
"gram.noun.commit.gender": "",
|
"lose": { "base": "lose", "past": "lost", "gerund": "losing" },
|
||||||
"gram.noun.commit.one": "commit",
|
"win": { "base": "win", "past": "won", "gerund": "winning" },
|
||||||
"gram.noun.commit.other": "commits",
|
"meet": { "base": "meet", "past": "met", "gerund": "meeting" },
|
||||||
"gram.noun.dependency.one": "dependency",
|
"lead": { "base": "lead", "past": "led", "gerund": "leading" },
|
||||||
"gram.noun.dependency.other": "dependencies",
|
"leave": { "base": "leave", "past": "left", "gerund": "leaving" },
|
||||||
"gram.noun.directory.one": "directory",
|
"spend": { "base": "spend", "past": "spent", "gerund": "spending" },
|
||||||
"gram.noun.directory.other": "directories",
|
"pay": { "base": "pay", "past": "paid", "gerund": "paying" },
|
||||||
"gram.noun.failed.one": "failed",
|
"sell": { "base": "sell", "past": "sold", "gerund": "selling" },
|
||||||
"gram.noun.failed.other": "failed",
|
"commit": { "base": "commit", "past": "committed", "gerund": "committing" },
|
||||||
"gram.noun.file.gender": "",
|
"stop": { "base": "stop", "past": "stopped", "gerund": "stopping" },
|
||||||
"gram.noun.file.one": "file",
|
"scan": { "base": "scan", "past": "scanned", "gerund": "scanning" },
|
||||||
"gram.noun.file.other": "files",
|
"format": { "base": "format", "past": "formatted", "gerund": "formatting" },
|
||||||
"gram.noun.issue.one": "issue",
|
"set": { "base": "set", "past": "set", "gerund": "setting" },
|
||||||
"gram.noun.issue.other": "issues",
|
"put": { "base": "put", "past": "put", "gerund": "putting" },
|
||||||
"gram.noun.item.gender": "",
|
"cut": { "base": "cut", "past": "cut", "gerund": "cutting" },
|
||||||
"gram.noun.item.one": "item",
|
"hit": { "base": "hit", "past": "hit", "gerund": "hitting" },
|
||||||
"gram.noun.item.other": "items",
|
"sit": { "base": "sit", "past": "sat", "gerund": "sitting" },
|
||||||
"gram.noun.package.one": "package",
|
"split": { "base": "split", "past": "split", "gerund": "splitting" },
|
||||||
"gram.noun.package.other": "packages",
|
"shut": { "base": "shut", "past": "shut", "gerund": "shutting" },
|
||||||
"gram.noun.passed.one": "passed",
|
"check": { "base": "check", "past": "checked", "gerund": "checking" },
|
||||||
"gram.noun.passed.other": "passed",
|
"create": { "base": "create", "past": "created", "gerund": "creating" },
|
||||||
"gram.noun.person.one": "person",
|
"delete": { "base": "delete", "past": "deleted", "gerund": "deleting" },
|
||||||
"gram.noun.person.other": "people",
|
"install": { "base": "install", "past": "installed", "gerund": "installing" },
|
||||||
"gram.noun.query.one": "query",
|
"update": { "base": "update", "past": "updated", "gerund": "updating" },
|
||||||
"gram.noun.query.other": "queries",
|
"pull": { "base": "pull", "past": "pulled", "gerund": "pulling" },
|
||||||
"gram.noun.repo.gender": "",
|
"push": { "base": "push", "past": "pushed", "gerund": "pushing" },
|
||||||
"gram.noun.repo.one": "repo",
|
"save": { "base": "save", "past": "saved", "gerund": "saving" },
|
||||||
"gram.noun.repo.other": "repos",
|
"analyse": { "base": "analyse", "past": "analysed", "gerund": "analysing" },
|
||||||
"gram.noun.repository.one": "repository",
|
"organise": { "base": "organise", "past": "organised", "gerund": "organising" },
|
||||||
"gram.noun.repository.other": "repositories",
|
"realise": { "base": "realise", "past": "realised", "gerund": "realising" },
|
||||||
"gram.noun.skipped.one": "skipped",
|
"recognise": { "base": "recognise", "past": "recognised", "gerund": "recognising" }
|
||||||
"gram.noun.skipped.other": "skipped",
|
},
|
||||||
"gram.noun.task.one": "task",
|
"noun": {
|
||||||
"gram.noun.task.other": "tasks",
|
"file": { "one": "file", "other": "files" },
|
||||||
"gram.noun.test.one": "test",
|
"repo": { "one": "repo", "other": "repos" },
|
||||||
"gram.noun.test.other": "tests",
|
"repository": { "one": "repository", "other": "repositories" },
|
||||||
"gram.noun.vulnerability.one": "vulnerability",
|
"commit": { "one": "commit", "other": "commits" },
|
||||||
"gram.noun.vulnerability.other": "vulnerabilities",
|
"branch": { "one": "branch", "other": "branches" },
|
||||||
"gram.number.decimal": ".",
|
"change": { "one": "change", "other": "changes" },
|
||||||
"gram.number.percent": "%s%%",
|
"item": { "one": "item", "other": "items" },
|
||||||
"gram.number.thousands": ",",
|
"issue": { "one": "issue", "other": "issues" },
|
||||||
"gram.punct.label": ":",
|
"task": { "one": "task", "other": "tasks" },
|
||||||
"gram.punct.progress": "...",
|
"person": { "one": "person", "other": "people" },
|
||||||
"gram.verb.analyse.base": "",
|
"child": { "one": "child", "other": "children" },
|
||||||
"gram.verb.analyse.gerund": "",
|
"package": { "one": "package", "other": "packages" },
|
||||||
"gram.verb.analyse.past": "",
|
"artifact": { "one": "artifact", "other": "artifacts" },
|
||||||
"gram.verb.be.base": "be",
|
"vulnerability": { "one": "vulnerability", "other": "vulnerabilities" },
|
||||||
"gram.verb.be.gerund": "being",
|
"dependency": { "one": "dependency", "other": "dependencies" },
|
||||||
"gram.verb.be.past": "was",
|
"directory": { "one": "directory", "other": "directories" },
|
||||||
"gram.verb.begin.base": "begin",
|
"category": { "one": "category", "other": "categories" },
|
||||||
"gram.verb.begin.gerund": "beginning",
|
"query": { "one": "query", "other": "queries" },
|
||||||
"gram.verb.begin.past": "began",
|
"check": { "one": "check", "other": "checks" },
|
||||||
"gram.verb.bring.base": "bring",
|
"test": { "one": "test", "other": "tests" }
|
||||||
"gram.verb.bring.gerund": "bringing",
|
},
|
||||||
"gram.verb.bring.past": "brought",
|
"article": {
|
||||||
"gram.verb.build.base": "build",
|
"indefinite": { "default": "a", "vowel": "an" },
|
||||||
"gram.verb.build.gerund": "building",
|
"definite": "the"
|
||||||
"gram.verb.build.past": "built",
|
},
|
||||||
"gram.verb.buy.base": "buy",
|
"word": {
|
||||||
"gram.verb.buy.gerund": "buying",
|
"url": "URL", "id": "ID", "ok": "OK", "ci": "CI", "qa": "QA",
|
||||||
"gram.verb.buy.past": "bought",
|
"php": "PHP", "sdk": "SDK", "html": "HTML", "cgo": "CGO", "pid": "PID",
|
||||||
"gram.verb.catch.base": "catch",
|
"cpus": "CPUs", "ssh": "SSH", "ssl": "SSL", "api": "API", "pr": "PR",
|
||||||
"gram.verb.catch.gerund": "catching",
|
"vite": "Vite", "pnpm": "pnpm",
|
||||||
"gram.verb.catch.past": "caught",
|
"app_url": "app URL", "blocked_by": "blocked by", "claimed_by": "claimed by",
|
||||||
"gram.verb.check.base": "",
|
"related_files": "related files", "up_to_date": "up to date",
|
||||||
"gram.verb.check.gerund": "",
|
"dry_run": "dry run", "go_mod": "go.mod",
|
||||||
"gram.verb.check.past": "",
|
"coverage": "coverage", "failed": "failed", "filter": "filter",
|
||||||
"gram.verb.choose.base": "choose",
|
"package": "package", "passed": "passed", "skipped": "skipped", "test": "test"
|
||||||
"gram.verb.choose.gerund": "choosing",
|
},
|
||||||
"gram.verb.choose.past": "chose",
|
"punct": {
|
||||||
"gram.verb.commit.base": "commit",
|
"label": ":",
|
||||||
"gram.verb.commit.gerund": "committing",
|
"progress": "..."
|
||||||
"gram.verb.commit.past": "committed",
|
},
|
||||||
"gram.verb.create.base": "",
|
"number": {
|
||||||
"gram.verb.create.gerund": "",
|
"thousands": ",",
|
||||||
"gram.verb.create.past": "",
|
"decimal": ".",
|
||||||
"gram.verb.cut.base": "cut",
|
"percent": "%s%%"
|
||||||
"gram.verb.cut.gerund": "cutting",
|
}
|
||||||
"gram.verb.cut.past": "cut",
|
},
|
||||||
"gram.verb.delete.base": "",
|
|
||||||
"gram.verb.delete.gerund": "",
|
"lang": {
|
||||||
"gram.verb.delete.past": "",
|
"de": "German", "en": "English", "es": "Spanish",
|
||||||
"gram.verb.do.base": "do",
|
"fr": "French", "ru": "Russian", "zh": "Chinese"
|
||||||
"gram.verb.do.gerund": "doing",
|
},
|
||||||
"gram.verb.do.past": "did",
|
|
||||||
"gram.verb.find.base": "find",
|
"prompt": {
|
||||||
"gram.verb.find.gerund": "finding",
|
"yes": "y", "no": "n",
|
||||||
"gram.verb.find.past": "found",
|
"continue": "Continue?", "proceed": "Proceed?",
|
||||||
"gram.verb.format.base": "format",
|
"confirm": "Are you sure?", "overwrite": "Overwrite?",
|
||||||
"gram.verb.format.gerund": "formatting",
|
"discard": "Discard changes?"
|
||||||
"gram.verb.format.past": "formatted",
|
},
|
||||||
"gram.verb.get.base": "get",
|
|
||||||
"gram.verb.get.gerund": "getting",
|
"time": {
|
||||||
"gram.verb.get.past": "got",
|
"just_now": "just now",
|
||||||
"gram.verb.go.base": "go",
|
"ago": {
|
||||||
"gram.verb.go.gerund": "going",
|
"second": { "one": "{{.Count}} second ago", "other": "{{.Count}} seconds ago" },
|
||||||
"gram.verb.go.past": "went",
|
"minute": { "one": "{{.Count}} minute ago", "other": "{{.Count}} minutes ago" },
|
||||||
"gram.verb.have.base": "have",
|
"hour": { "one": "{{.Count}} hour ago", "other": "{{.Count}} hours ago" },
|
||||||
"gram.verb.have.gerund": "having",
|
"day": { "one": "{{.Count}} day ago", "other": "{{.Count}} days ago" },
|
||||||
"gram.verb.have.past": "had",
|
"week": { "one": "{{.Count}} week ago", "other": "{{.Count}} weeks ago" }
|
||||||
"gram.verb.hit.base": "hit",
|
}
|
||||||
"gram.verb.hit.gerund": "hitting",
|
}
|
||||||
"gram.verb.hit.past": "hit",
|
|
||||||
"gram.verb.hold.base": "hold",
|
|
||||||
"gram.verb.hold.gerund": "holding",
|
|
||||||
"gram.verb.hold.past": "held",
|
|
||||||
"gram.verb.install.base": "",
|
|
||||||
"gram.verb.install.gerund": "",
|
|
||||||
"gram.verb.install.past": "",
|
|
||||||
"gram.verb.keep.base": "keep",
|
|
||||||
"gram.verb.keep.gerund": "keeping",
|
|
||||||
"gram.verb.keep.past": "kept",
|
|
||||||
"gram.verb.lead.base": "lead",
|
|
||||||
"gram.verb.lead.gerund": "leading",
|
|
||||||
"gram.verb.lead.past": "led",
|
|
||||||
"gram.verb.leave.base": "leave",
|
|
||||||
"gram.verb.leave.gerund": "leaving",
|
|
||||||
"gram.verb.leave.past": "left",
|
|
||||||
"gram.verb.lose.base": "lose",
|
|
||||||
"gram.verb.lose.gerund": "losing",
|
|
||||||
"gram.verb.lose.past": "lost",
|
|
||||||
"gram.verb.make.base": "make",
|
|
||||||
"gram.verb.make.gerund": "making",
|
|
||||||
"gram.verb.make.past": "made",
|
|
||||||
"gram.verb.meet.base": "meet",
|
|
||||||
"gram.verb.meet.gerund": "meeting",
|
|
||||||
"gram.verb.meet.past": "met",
|
|
||||||
"gram.verb.organise.base": "",
|
|
||||||
"gram.verb.organise.gerund": "",
|
|
||||||
"gram.verb.organise.past": "",
|
|
||||||
"gram.verb.pay.base": "pay",
|
|
||||||
"gram.verb.pay.gerund": "paying",
|
|
||||||
"gram.verb.pay.past": "paid",
|
|
||||||
"gram.verb.pull.base": "",
|
|
||||||
"gram.verb.pull.gerund": "",
|
|
||||||
"gram.verb.pull.past": "",
|
|
||||||
"gram.verb.push.base": "",
|
|
||||||
"gram.verb.push.gerund": "",
|
|
||||||
"gram.verb.push.past": "",
|
|
||||||
"gram.verb.put.base": "put",
|
|
||||||
"gram.verb.put.gerund": "putting",
|
|
||||||
"gram.verb.put.past": "put",
|
|
||||||
"gram.verb.realise.base": "",
|
|
||||||
"gram.verb.realise.gerund": "",
|
|
||||||
"gram.verb.realise.past": "",
|
|
||||||
"gram.verb.recognise.base": "",
|
|
||||||
"gram.verb.recognise.gerund": "",
|
|
||||||
"gram.verb.recognise.past": "",
|
|
||||||
"gram.verb.run.base": "run",
|
|
||||||
"gram.verb.run.gerund": "running",
|
|
||||||
"gram.verb.run.past": "ran",
|
|
||||||
"gram.verb.save.base": "",
|
|
||||||
"gram.verb.save.gerund": "",
|
|
||||||
"gram.verb.save.past": "",
|
|
||||||
"gram.verb.scan.base": "scan",
|
|
||||||
"gram.verb.scan.gerund": "scanning",
|
|
||||||
"gram.verb.scan.past": "scanned",
|
|
||||||
"gram.verb.sell.base": "sell",
|
|
||||||
"gram.verb.sell.gerund": "selling",
|
|
||||||
"gram.verb.sell.past": "sold",
|
|
||||||
"gram.verb.send.base": "send",
|
|
||||||
"gram.verb.send.gerund": "sending",
|
|
||||||
"gram.verb.send.past": "sent",
|
|
||||||
"gram.verb.set.base": "set",
|
|
||||||
"gram.verb.set.gerund": "setting",
|
|
||||||
"gram.verb.set.past": "set",
|
|
||||||
"gram.verb.shut.base": "shut",
|
|
||||||
"gram.verb.shut.gerund": "shutting",
|
|
||||||
"gram.verb.shut.past": "shut",
|
|
||||||
"gram.verb.sit.base": "sit",
|
|
||||||
"gram.verb.sit.gerund": "sitting",
|
|
||||||
"gram.verb.sit.past": "sat",
|
|
||||||
"gram.verb.spend.base": "spend",
|
|
||||||
"gram.verb.spend.gerund": "spending",
|
|
||||||
"gram.verb.spend.past": "spent",
|
|
||||||
"gram.verb.split.base": "split",
|
|
||||||
"gram.verb.split.gerund": "splitting",
|
|
||||||
"gram.verb.split.past": "split",
|
|
||||||
"gram.verb.stop.base": "stop",
|
|
||||||
"gram.verb.stop.gerund": "stopping",
|
|
||||||
"gram.verb.stop.past": "stopped",
|
|
||||||
"gram.verb.take.base": "take",
|
|
||||||
"gram.verb.take.gerund": "taking",
|
|
||||||
"gram.verb.take.past": "took",
|
|
||||||
"gram.verb.think.base": "think",
|
|
||||||
"gram.verb.think.gerund": "thinking",
|
|
||||||
"gram.verb.think.past": "thought",
|
|
||||||
"gram.verb.update.base": "",
|
|
||||||
"gram.verb.update.gerund": "",
|
|
||||||
"gram.verb.update.past": "",
|
|
||||||
"gram.verb.win.base": "win",
|
|
||||||
"gram.verb.win.gerund": "winning",
|
|
||||||
"gram.verb.win.past": "won",
|
|
||||||
"gram.verb.write.base": "write",
|
|
||||||
"gram.verb.write.gerund": "writing",
|
|
||||||
"gram.verb.write.past": "wrote",
|
|
||||||
"gram.word.api": "API",
|
|
||||||
"gram.word.app_url": "app URL",
|
|
||||||
"gram.word.blocked_by": "blocked by",
|
|
||||||
"gram.word.cgo": "CGO",
|
|
||||||
"gram.word.ci": "CI",
|
|
||||||
"gram.word.claimed_by": "claimed by",
|
|
||||||
"gram.word.coverage": "coverage",
|
|
||||||
"gram.word.cpus": "CPUs",
|
|
||||||
"gram.word.dry_run": "dry run",
|
|
||||||
"gram.word.failed": "failed",
|
|
||||||
"gram.word.filter": "filter",
|
|
||||||
"gram.word.go_mod": "go.mod",
|
|
||||||
"gram.word.html": "HTML",
|
|
||||||
"gram.word.id": "ID",
|
|
||||||
"gram.word.ok": "OK",
|
|
||||||
"gram.word.package": "package",
|
|
||||||
"gram.word.passed": "passed",
|
|
||||||
"gram.word.php": "PHP",
|
|
||||||
"gram.word.pid": "PID",
|
|
||||||
"gram.word.pnpm": "pnpm",
|
|
||||||
"gram.word.pr": "PR",
|
|
||||||
"gram.word.qa": "QA",
|
|
||||||
"gram.word.related_files": "related files",
|
|
||||||
"gram.word.sdk": "SDK",
|
|
||||||
"gram.word.skipped": "skipped",
|
|
||||||
"gram.word.ssh": "SSH",
|
|
||||||
"gram.word.ssl": "SSL",
|
|
||||||
"gram.word.test": "test",
|
|
||||||
"gram.word.up_to_date": "up to date",
|
|
||||||
"gram.word.url": "URL",
|
|
||||||
"gram.word.vite": "Vite",
|
|
||||||
"lang.de": "German",
|
|
||||||
"lang.en": "English",
|
|
||||||
"lang.es": "Spanish",
|
|
||||||
"lang.fr": "French",
|
|
||||||
"lang.zh": "Chinese",
|
|
||||||
"prompt.confirm": "Are you sure?",
|
|
||||||
"prompt.continue": "Continue?",
|
|
||||||
"prompt.discard": "Discard changes?",
|
|
||||||
"prompt.no": "n",
|
|
||||||
"prompt.overwrite": "Overwrite?",
|
|
||||||
"prompt.proceed": "Proceed?",
|
|
||||||
"prompt.yes": "y",
|
|
||||||
"time.ago.day.one": "{{.Count}} day ago",
|
|
||||||
"time.ago.day.other": "{{.Count}} days ago",
|
|
||||||
"time.ago.hour.one": "{{.Count}} hour ago",
|
|
||||||
"time.ago.hour.other": "{{.Count}} hours ago",
|
|
||||||
"time.ago.minute.one": "{{.Count}} minute ago",
|
|
||||||
"time.ago.minute.other": "{{.Count}} minutes ago",
|
|
||||||
"time.ago.second.one": "{{.Count}} second ago",
|
|
||||||
"time.ago.second.other": "{{.Count}} seconds ago",
|
|
||||||
"time.ago.week.one": "{{.Count}} week ago",
|
|
||||||
"time.ago.week.other": "{{.Count}} weeks ago",
|
|
||||||
"time.just_now": "just now"
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1 +1,148 @@
|
||||||
{}
|
{
|
||||||
|
"gram": {
|
||||||
|
"verb": {
|
||||||
|
"be": { "base": "是", "past": "是", "gerund": "状态" },
|
||||||
|
"go": { "base": "前往", "past": "前往", "gerund": "前往" },
|
||||||
|
"do": { "base": "执行", "past": "执行", "gerund": "执行" },
|
||||||
|
"have": { "base": "拥有", "past": "拥有", "gerund": "拥有" },
|
||||||
|
"make": { "base": "创建", "past": "创建", "gerund": "创建" },
|
||||||
|
"get": { "base": "获取", "past": "获取", "gerund": "获取" },
|
||||||
|
"run": { "base": "运行", "past": "运行", "gerund": "运行" },
|
||||||
|
"write": { "base": "写入", "past": "写入", "gerund": "写入" },
|
||||||
|
"build": { "base": "构建", "past": "构建", "gerund": "构建" },
|
||||||
|
"send": { "base": "发送", "past": "发送", "gerund": "发送" },
|
||||||
|
"find": { "base": "查找", "past": "查找", "gerund": "查找" },
|
||||||
|
"take": { "base": "获取", "past": "获取", "gerund": "获取" },
|
||||||
|
"begin": { "base": "开始", "past": "开始", "gerund": "开始" },
|
||||||
|
"keep": { "base": "保持", "past": "保持", "gerund": "保持" },
|
||||||
|
"hold": { "base": "持有", "past": "持有", "gerund": "持有" },
|
||||||
|
"bring": { "base": "带来", "past": "带来", "gerund": "带来" },
|
||||||
|
"think": { "base": "思考", "past": "思考", "gerund": "思考" },
|
||||||
|
"choose": { "base": "选择", "past": "选择", "gerund": "选择" },
|
||||||
|
"lose": { "base": "丢失", "past": "丢失", "gerund": "丢失" },
|
||||||
|
"win": { "base": "成功", "past": "成功", "gerund": "成功" },
|
||||||
|
"meet": { "base": "匹配", "past": "匹配", "gerund": "匹配" },
|
||||||
|
"lead": { "base": "引导", "past": "引导", "gerund": "引导" },
|
||||||
|
"leave": { "base": "离开", "past": "离开", "gerund": "离开" },
|
||||||
|
"commit": { "base": "提交", "past": "提交", "gerund": "提交" },
|
||||||
|
"stop": { "base": "停止", "past": "停止", "gerund": "停止" },
|
||||||
|
"scan": { "base": "扫描", "past": "扫描", "gerund": "扫描" },
|
||||||
|
"format": { "base": "格式化", "past": "格式化", "gerund": "格式化" },
|
||||||
|
"set": { "base": "设置", "past": "设置", "gerund": "设置" },
|
||||||
|
"check": { "base": "检查", "past": "检查", "gerund": "检查" },
|
||||||
|
"create": { "base": "创建", "past": "创建", "gerund": "创建" },
|
||||||
|
"delete": { "base": "删除", "past": "删除", "gerund": "删除" },
|
||||||
|
"install": { "base": "安装", "past": "安装", "gerund": "安装" },
|
||||||
|
"update": { "base": "更新", "past": "更新", "gerund": "更新" },
|
||||||
|
"pull": { "base": "拉取", "past": "拉取", "gerund": "拉取" },
|
||||||
|
"push": { "base": "推送", "past": "推送", "gerund": "推送" },
|
||||||
|
"save": { "base": "保存", "past": "保存", "gerund": "保存" },
|
||||||
|
"analyse": { "base": "分析", "past": "分析", "gerund": "分析" },
|
||||||
|
"organise": { "base": "整理", "past": "整理", "gerund": "整理" },
|
||||||
|
"test": { "base": "测试", "past": "测试", "gerund": "测试" },
|
||||||
|
"deploy": { "base": "部署", "past": "部署", "gerund": "部署" },
|
||||||
|
"clone": { "base": "克隆", "past": "克隆", "gerund": "克隆" },
|
||||||
|
"compile": { "base": "编译", "past": "编译", "gerund": "编译" },
|
||||||
|
"download": { "base": "下载", "past": "下载", "gerund": "下载" },
|
||||||
|
"upload": { "base": "上传", "past": "上传", "gerund": "上传" }
|
||||||
|
},
|
||||||
|
"noun": {
|
||||||
|
"file": { "one": "文件", "other": "文件" },
|
||||||
|
"repo": { "one": "仓库", "other": "仓库" },
|
||||||
|
"repository": { "one": "仓库", "other": "仓库" },
|
||||||
|
"commit": { "one": "提交", "other": "提交" },
|
||||||
|
"branch": { "one": "分支", "other": "分支" },
|
||||||
|
"change": { "one": "更改", "other": "更改" },
|
||||||
|
"item": { "one": "项", "other": "项" },
|
||||||
|
"issue": { "one": "问题", "other": "问题" },
|
||||||
|
"task": { "one": "任务", "other": "任务" },
|
||||||
|
"person": { "one": "人", "other": "人" },
|
||||||
|
"child": { "one": "子项", "other": "子项" },
|
||||||
|
"package": { "one": "包", "other": "包" },
|
||||||
|
"artifact": { "one": "构件", "other": "构件" },
|
||||||
|
"vulnerability": { "one": "漏洞", "other": "漏洞" },
|
||||||
|
"dependency": { "one": "依赖", "other": "依赖" },
|
||||||
|
"directory": { "one": "目录", "other": "目录" },
|
||||||
|
"category": { "one": "分类", "other": "分类" },
|
||||||
|
"query": { "one": "查询", "other": "查询" },
|
||||||
|
"check": { "one": "检查", "other": "检查" },
|
||||||
|
"test": { "one": "测试", "other": "测试" },
|
||||||
|
"error": { "one": "错误", "other": "错误" },
|
||||||
|
"warning": { "one": "警告", "other": "警告" },
|
||||||
|
"service": { "one": "服务", "other": "服务" },
|
||||||
|
"config": { "one": "配置", "other": "配置" },
|
||||||
|
"workflow": { "one": "工作流", "other": "工作流" }
|
||||||
|
},
|
||||||
|
"article": {
|
||||||
|
"indefinite": { "default": "", "vowel": "" },
|
||||||
|
"definite": ""
|
||||||
|
},
|
||||||
|
"word": {
|
||||||
|
"url": "URL", "id": "ID", "ok": "OK", "ci": "CI", "qa": "QA",
|
||||||
|
"php": "PHP", "sdk": "SDK", "html": "HTML", "cgo": "CGO", "pid": "PID",
|
||||||
|
"cpus": "CPU", "ssh": "SSH", "ssl": "SSL", "api": "API", "pr": "PR",
|
||||||
|
"vite": "Vite", "pnpm": "pnpm",
|
||||||
|
"app_url": "应用 URL", "blocked_by": "被阻塞",
|
||||||
|
"claimed_by": "已认领", "related_files": "相关文件",
|
||||||
|
"up_to_date": "已是最新", "dry_run": "模拟运行",
|
||||||
|
"go_mod": "go.mod", "coverage": "覆盖率", "failed": "失败",
|
||||||
|
"filter": "过滤器", "package": "包", "passed": "通过",
|
||||||
|
"skipped": "跳过", "test": "测试"
|
||||||
|
},
|
||||||
|
"punct": {
|
||||||
|
"label": ":",
|
||||||
|
"progress": "..."
|
||||||
|
},
|
||||||
|
"number": {
|
||||||
|
"thousands": ",",
|
||||||
|
"decimal": ".",
|
||||||
|
"percent": "%s%%"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"cli.aborted": "已中止。",
|
||||||
|
"cli.fail": "失败",
|
||||||
|
"cli.pass": "通过",
|
||||||
|
|
||||||
|
"lang": {
|
||||||
|
"de": "德语", "en": "英语", "es": "西班牙语",
|
||||||
|
"fr": "法语", "ru": "俄语", "zh": "中文"
|
||||||
|
},
|
||||||
|
|
||||||
|
"prompt": {
|
||||||
|
"yes": "是", "no": "否",
|
||||||
|
"continue": "继续?", "proceed": "执行?",
|
||||||
|
"confirm": "确定吗?", "overwrite": "覆盖?",
|
||||||
|
"discard": "放弃更改?"
|
||||||
|
},
|
||||||
|
|
||||||
|
"time": {
|
||||||
|
"just_now": "刚刚",
|
||||||
|
"ago": {
|
||||||
|
"second": { "other": "{{.Count}} 秒前" },
|
||||||
|
"minute": { "other": "{{.Count}} 分钟前" },
|
||||||
|
"hour": { "other": "{{.Count}} 小时前" },
|
||||||
|
"day": { "other": "{{.Count}} 天前" },
|
||||||
|
"week": { "other": "{{.Count}} 周前" }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"error.gh_not_found": "未找到 'gh' CLI 工具。请安装:https://cli.github.com/",
|
||||||
|
"error.registry_not_found": "未找到 repos.yaml",
|
||||||
|
"error.repo_not_found": "未找到仓库 '{{.Name}}'",
|
||||||
|
|
||||||
|
"common.label.done": "完成",
|
||||||
|
"common.label.error": "错误",
|
||||||
|
"common.label.info": "信息",
|
||||||
|
"common.label.success": "成功",
|
||||||
|
"common.label.warning": "警告",
|
||||||
|
"common.status.clean": "干净",
|
||||||
|
"common.status.dirty": "已修改",
|
||||||
|
"common.status.running": "运行中",
|
||||||
|
"common.status.stopped": "已停止",
|
||||||
|
"common.status.up_to_date": "已是最新",
|
||||||
|
"common.result.all_passed": "所有测试通过",
|
||||||
|
"common.result.no_issues": "未发现问题",
|
||||||
|
"common.prompt.abort": "已中止。",
|
||||||
|
"common.success.completed": "{{.Action}} 成功完成"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -201,7 +201,7 @@ else
|
||||||
|
|
||||||
# Install from releases
|
# Install from releases
|
||||||
ARCH=$(dpkg --print-architecture)
|
ARCH=$(dpkg --print-architecture)
|
||||||
CORE_URL="https://github.com/host-uk/core/releases/latest/download/core-linux-${ARCH}"
|
CORE_URL="https://forge.lthn.ai/core/cli/releases/latest/download/core-linux-${ARCH}"
|
||||||
|
|
||||||
curl -fsSL "$CORE_URL" -o /tmp/core
|
curl -fsSL "$CORE_URL" -o /tmp/core
|
||||||
chmod +x /tmp/core
|
chmod +x /tmp/core
|
||||||
|
|
@ -220,7 +220,7 @@ if [[ -z "${SKIP_GUI:-}" ]]; then
|
||||||
log_info "Installing core-ide..."
|
log_info "Installing core-ide..."
|
||||||
|
|
||||||
ARCH=$(dpkg --print-architecture)
|
ARCH=$(dpkg --print-architecture)
|
||||||
IDE_URL="https://github.com/host-uk/core/releases/latest/download/core-ide-linux-${ARCH}.deb"
|
IDE_URL="https://forge.lthn.ai/core/cli/releases/latest/download/core-ide-linux-${ARCH}.deb"
|
||||||
|
|
||||||
curl -fsSL "$IDE_URL" -o /tmp/core-ide.deb
|
curl -fsSL "$IDE_URL" -o /tmp/core-ide.deb
|
||||||
sudo dpkg -i /tmp/core-ide.deb || sudo apt-get install -f -y
|
sudo dpkg -i /tmp/core-ide.deb || sudo apt-get install -f -y
|
||||||
|
|
|
||||||
|
|
@ -491,7 +491,7 @@ In `pkg/build/config.go`, add to the `BuildConfig` struct:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// Add import
|
// Add import
|
||||||
import "github.com/host-uk/core/pkg/build/signing"
|
import "forge.lthn.ai/core/cli/pkg/build/signing"
|
||||||
|
|
||||||
// Add to BuildConfig struct after Targets field:
|
// Add to BuildConfig struct after Targets field:
|
||||||
// Sign contains code signing configuration.
|
// Sign contains code signing configuration.
|
||||||
|
|
@ -590,7 +590,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/build"
|
"forge.lthn.ai/core/cli/pkg/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SignBinaries signs macOS binaries in the artifacts list.
|
// SignBinaries signs macOS binaries in the artifacts list.
|
||||||
|
|
@ -727,7 +727,7 @@ buildCmd.Action(func() error {
|
||||||
Add to imports:
|
Add to imports:
|
||||||
|
|
||||||
```go
|
```go
|
||||||
"github.com/host-uk/core/pkg/build/signing"
|
"forge.lthn.ai/core/cli/pkg/build/signing"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Step 4: Add signing after build, before archive**
|
**Step 4: Add signing after build, before archive**
|
||||||
|
|
@ -820,7 +820,7 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/build"
|
"forge.lthn.ai/core/cli/pkg/build"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSignBinaries_Good_SkipsNonDarwin(t *testing.T) {
|
func TestSignBinaries_Good_SkipsNonDarwin(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -19,17 +19,17 @@
|
||||||
**Step 1: Create go.mod**
|
**Step 1: Create go.mod**
|
||||||
|
|
||||||
```go
|
```go
|
||||||
module github.com/host-uk/core/pkg/devops
|
module forge.lthn.ai/core/cli/pkg/devops
|
||||||
|
|
||||||
go 1.25
|
go 1.25
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/host-uk/core/pkg/container v0.0.0
|
forge.lthn.ai/core/cli/pkg/container v0.0.0
|
||||||
golang.org/x/crypto v0.32.0
|
golang.org/x/crypto v0.32.0
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/host-uk/core/pkg/container => ../container
|
replace forge.lthn.ai/core/cli/pkg/container => ../container
|
||||||
```
|
```
|
||||||
|
|
||||||
**Step 2: Create devops.go with core types**
|
**Step 2: Create devops.go with core types**
|
||||||
|
|
@ -45,7 +45,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/container"
|
"forge.lthn.ai/core/cli/pkg/container"
|
||||||
)
|
)
|
||||||
|
|
||||||
// DevOps manages the portable development environment.
|
// DevOps manages the portable development environment.
|
||||||
|
|
@ -744,7 +744,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/devops/sources"
|
"forge.lthn.ai/core/cli/pkg/devops/sources"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ImageManager handles image downloads and updates.
|
// ImageManager handles image downloads and updates.
|
||||||
|
|
@ -1786,7 +1786,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/host-uk/core/pkg/devops"
|
"forge.lthn.ai/core/cli/pkg/devops"
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
**Step 1: Create go.mod for sdk package**
|
**Step 1: Create go.mod for sdk package**
|
||||||
|
|
||||||
```go
|
```go
|
||||||
module github.com/host-uk/core/pkg/sdk
|
module forge.lthn.ai/core/cli/pkg/sdk
|
||||||
|
|
||||||
go 1.25
|
go 1.25
|
||||||
|
|
||||||
|
|
@ -1390,7 +1390,7 @@ Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>"
|
||||||
// Add to sdk.go, replacing the stub Generate method
|
// Add to sdk.go, replacing the stub Generate method
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/host-uk/core/pkg/sdk/generators"
|
"forge.lthn.ai/core/cli/pkg/sdk/generators"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Generate generates SDKs for all configured languages.
|
// Generate generates SDKs for all configured languages.
|
||||||
|
|
@ -1494,7 +1494,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/charmbracelet/lipgloss"
|
"github.com/charmbracelet/lipgloss"
|
||||||
"github.com/host-uk/core/pkg/sdk"
|
"forge.lthn.ai/core/cli/pkg/sdk"
|
||||||
"github.com/leaanthony/clir"
|
"github.com/leaanthony/clir"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/host-uk/core/pkg/sdk"
|
"forge.lthn.ai/core/cli/pkg/sdk"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SDKRelease holds the result of an SDK release.
|
// SDKRelease holds the result of an SDK release.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue