diff --git a/.gitignore b/.gitignore index 9ba5b87..869291d 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ opencode-config/package-lock.json internal/preproc/lib/ .bun/ *.asm +.config/ diff --git a/BUILDNUM b/BUILDNUM new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/BUILDNUM @@ -0,0 +1 @@ +1 diff --git a/Dockerfile b/Dockerfile index 2f29229..0640b5e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,5 @@ FROM ghcr.io/anomalyco/opencode:1.14.48 +#FROM ghcr.io/anomalyco/opencode:latest RUN apk add --no-cache go gcc musl-dev diff --git a/README.md b/README.md index cda2058..97ef35b 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,35 @@ A high-level 6502 cross-compiler targeting the ACME Cross-Assembler. c65gm provides a more expressive language for writing 6502 assembly programs, with features like functions, type-checked variables, control flow structures, and compile-time optimizations. +## Download + +Pre-built binaries are available from the [Releases page](https://github.com/YOUR_USERNAME/c65gm/releases). Each release follows the naming pattern `c65gm_v{MAJOR}.{BUILD}_os_arch`: + +| File | Platform | +|------|----------| +| `c65gm_v1.1_linux_amd64.tar.gz` | Linux (x86_64) | +| `c65gm_v1.1_linux_arm64.tar.gz` | Linux (ARM64) | +| `c65gm_v1.1_darwin_amd64.tar.gz` | macOS (Intel) | +| `c65gm_v1.1_darwin_arm64.tar.gz` | macOS (Apple Silicon) | +| `c65gm_v1.1_windows_amd64.zip` | Windows (x86_64) | + +Each release includes a `checksums.txt` file for verifying download integrity: + +```bash +sha256sum -c checksums.txt +``` + +### Verify with the annotated tag + +Every release is created from an annotated git tag containing the Go compiler version and SHA256 checksums. To inspect: + +```bash +git fetch --tags +git tag -l v1.1 +``` + +The tag message shows the exact checksums. You can reproduce the build from the tag and verify your binary matches: + ## What It Does c65gm compiles high-level source code into ACME assembler syntax for the 6502 processor (Commodore 64 and similar platforms). It provides: @@ -248,6 +277,68 @@ The example directories also contain `cm.sh` scripts showing the old build metho - Kate: copy XML to ~/.local/share/org.kde.syntax-highlighting/syntax/ - Sublime: copy .sublime-syntax to Packages/User/ +## Release Process + +This section documents how to create a new release for maintainers. + +### Prerequisites + +- Go 1.25.1+ installed +- `sha256sum` (Linux) or `shasum` (macOS) +- Git with write access to the repository + +### Steps + +The release script does everything automatically. Just run it: + +```bash +bash scripts/release.sh +``` + +This performs the following sequence: + +1. **Verify git clean** — fails if uncommitted changes exist +2. **Read BUILDNUM** — e.g., `42` → version `1.42` +3. **Verify tag available** — fails if tag `v1.42` already exists +4. **Bump BUILDNUM** — writes `43` to `BUILDNUM` +5. **Build all targets** — 5 platform binaries with version injected via ldflags +6. **Generate SHA256 checksums** — written to `dist/checksums.txt` +7. **Commit BUILDNUM** — `git commit` with checksums in the commit body +8. **Create annotated tag** — `git tag -a v1.42` pointing at the pre-bump commit, with checksums and Go version in the tag message + +### Publish + +```bash +git push --follow-tags origin +``` + +This pushes both the new commit and the annotated tag. The tag is the source of truth for verification. + +### Verify a release + +```bash +git checkout v1.42 +# You now have the exact source (BUILDNUM=42) +go build -trimpath -ldflags="-s -w -X main.version=1.42" +sha256sum c65gm +# Compare with the checksum in: git tag -l v1.42 +``` + +### Reproducible builds + +The release script uses these flags for deterministic builds: + +```bash +go build -trimpath -ldflags="-s -w -X main.version=${VERSION}" +``` + +- `-trimpath` strips local filesystem paths +- `-s -w` strips symbol table and DWARF debug info +- `-X main.version` injects the version +- `CGO_ENABLED=0` ensures fully static binaries + +Binaries built from the same source with the same Go version produce identical hashes. The tag message captures the Go version and expected checksums for verification. + ## License Copyright (C) 1999, 2025 Mattias Hansson diff --git a/main.go b/main.go index 87a4c1d..bfb58bb 100644 --- a/main.go +++ b/main.go @@ -20,6 +20,8 @@ import ( // Copyright (C) 1999, 2025 Mattias Hansson // Distributed under GPL. +var version = "dev" + // ANSI color codes for error messages const ( colorRed = "\033[31m" @@ -28,8 +30,14 @@ const ( ) func main() { + // Check for version flag before banner + if len(os.Args) == 2 && (os.Args[1] == "--version" || os.Args[1] == "-V") { + fmt.Printf("c65gm v%s\n", version) + return + } + fmt.Println("c65gm - A 6502 Cross-Compiler for the ACME Cross-Assembler.") - fmt.Println("Copyright (C) 1999, 2025 Mattias Hansson. v1.0.0") + fmt.Printf("Copyright (C) 1999, 2025 Mattias Hansson. v%s\n", version) fmt.Println("Distributed under GPL.") fmt.Println() @@ -222,6 +230,7 @@ func printUsage() { fmt.Println(" -o, --output Output file (default: .prg for build, .asm for compile)") fmt.Println(" --keep-asm Keep intermediate assembly file (build command only)") fmt.Println(" --no-cbm Don't add -f cbm flag to ACME (build command only)") + fmt.Println(" -V, --version Show version") } func runBuildCommand(args []string) { diff --git a/opencode-config/opencode.json b/opencode-config/opencode.json index 86da758..cc67ed9 100644 --- a/opencode-config/opencode.json +++ b/opencode-config/opencode.json @@ -2,12 +2,16 @@ "$schema": "https://opencode.ai/config.json", "agent": { "plan": { - "model": "deepseek/deepseek-reasoner", - "description": "Planning and architecture analysis using DeepSeek Reasoner" + "model": "deepseek/deepseek-v4-pro", + "options": { + "thinking": { "type": "enabled" }, + "reasoningEffort": "high" + }, + "description": "Planning and architecture analysis using DeepSeek V4 Pro" }, "build": { - "model": "deepseek/deepseek-chat", - "description": "Implementation and coding using DeepSeek Chat" + "model": "deepseek/deepseek-v4-flash", + "description": "Implementation and coding using DeepSeek V4 Flash" } } } \ No newline at end of file diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..cda0f0f --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,238 @@ +#!/bin/bash +set -euo pipefail + +# c65gm Release Builder +# +# Fully automated release process: +# 1. Verifies git working tree is clean +# 2. Reads BUILDNUM, derives version (MAJOR.BUILDNUM) +# 3. Verifies the tag doesn't already exist +# 4. Bumps BUILDNUM +# 5. Builds all target binaries (embedded lib, version injected) +# 6. Generates SHA256 checksums +# 7. Commits the BUILDNUM bump (with checksums in commit body) +# 8. Creates an annotated tag pointing at PRE-bump commit +# (tag message contains checksums + build info for verification) +# +# To publish the release: +# git push --follow-tags origin + +MAJOR=1 + +print_ok() { echo " [OK] $1"; } +print_err() { echo " [ERROR] $1" >&2; } + +echo "" +echo "=== c65gm Release Builder ===" +echo "" + +# --------------------------------------------------------------------------- +# 1. Verify git working tree is clean +# --------------------------------------------------------------------------- +if [ -n "$(git status --porcelain 2>/dev/null)" ]; then + echo "" + git status --short + echo "" + print_err "working tree is not clean — commit or stash changes first" + exit 1 +fi +print_ok "Working tree is clean" + +# --------------------------------------------------------------------------- +# 2. Read BUILDNUM +# --------------------------------------------------------------------------- +if [ ! -f BUILDNUM ]; then + echo "1" > BUILDNUM +fi + +BUILDNUM=$(cat BUILDNUM) +VERSION="${MAJOR}.${BUILDNUM}" +TAG="v${VERSION}" + +# --------------------------------------------------------------------------- +# 3. Verify tag doesn't already exist +# --------------------------------------------------------------------------- +if git rev-parse --quiet --verify "refs/tags/${TAG}" >/dev/null 2>&1; then + print_err "tag ${TAG} already exists — bump BUILDNUM manually if needed" + exit 1 +fi +print_ok "Tag ${TAG} is available" + +# --------------------------------------------------------------------------- +# 4. Save source commit (before any changes) +# --------------------------------------------------------------------------- +SOURCE_COMMIT=$(git rev-parse HEAD) +if [ -z "$SOURCE_COMMIT" ]; then + print_err "could not determine HEAD commit" + exit 1 +fi + +echo "" +echo " Version: ${TAG}" +echo " Source: ${SOURCE_COMMIT}" +echo " BUILDNUM: ${BUILDNUM} (bumps to $((BUILDNUM + 1)))" +echo "" + +# --------------------------------------------------------------------------- +# 5. Bump BUILDNUM +# --------------------------------------------------------------------------- +NEXT_BUILDNUM=$((BUILDNUM + 1)) +echo "${NEXT_BUILDNUM}" > BUILDNUM + +# --------------------------------------------------------------------------- +# 6. Copy lib for embedding +# --------------------------------------------------------------------------- +echo " Preparing embedded library..." +rm -rf internal/preproc/lib +cp -r lib internal/preproc/ +print_ok "Library copied for embedding" + +# --------------------------------------------------------------------------- +# Build metadata +# --------------------------------------------------------------------------- +GO_VERSION="$(go version 2>/dev/null || echo 'unknown')" +BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u +%Y-%m-%d)" + +LDFLAGS="-s -w -X main.version=${VERSION}" +DIST="dist" +mkdir -p "$DIST" + +# SHA256 command detection (Linux vs macOS) +SHA_CMD="" +if command -v sha256sum &>/dev/null; then + SHA_CMD="sha256sum" +elif command -v shasum &>/dev/null; then + SHA_CMD="shasum -a 256" +else + print_err "no sha256sum or shasum command found" + exit 1 +fi + +# Targets +TARGETS=( + "linux/amd64" + "linux/arm64" + "darwin/amd64" + "darwin/arm64" + "windows/amd64" +) + +echo "" +echo " Building targets: ${TARGETS[*]}" +echo "" + +# --------------------------------------------------------------------------- +# 7. Build all targets +# --------------------------------------------------------------------------- +BUILD_DIR="$(mktemp -d)" +trap 'rm -rf "$BUILD_DIR"' EXIT + +for target in "${TARGETS[@]}"; do + GOOS="${target%%/*}" + GOARCH="${target##*/}" + + bin_name="c65gm" + [ "$GOOS" = "windows" ] && bin_name="c65gm.exe" + + echo " ${target}..." + + GOOS=$GOOS GOARCH=$GOARCH CGO_ENABLED=0 \ + go build -trimpath -ldflags="$LDFLAGS" -o "${BUILD_DIR}/${bin_name}" + + archive_name="c65gm_v${VERSION}_${GOOS}_${GOARCH}" + if [ "$GOOS" = "windows" ]; then + (cd "$BUILD_DIR" && zip -q "${OLDPWD}/${DIST}/${archive_name}.zip" "$bin_name") + echo " => ${DIST}/${archive_name}.zip" + else + tar -czf "${DIST}/${archive_name}.tar.gz" -C "$BUILD_DIR" "$bin_name" + echo " => ${DIST}/${archive_name}.tar.gz" + fi + + rm -f "${BUILD_DIR}/${bin_name}" +done + +echo "" +print_ok "All targets built" + +# --------------------------------------------------------------------------- +# 8. Generate checksums +# --------------------------------------------------------------------------- +echo "" +echo "=== SHA256 checksums ===" +echo "" +( + cd "$DIST" + $SHA_CMD * > checksums.txt + cat checksums.txt +) + +CHECKSUMS_TEXT="$(cat "${DIST}/checksums.txt")" +print_ok "Checksums generated" + +# --------------------------------------------------------------------------- +# 9. Commit BUILDNUM bump +# --------------------------------------------------------------------------- +echo "" +echo "=== Creating commit ===" + +COMMIT_MSG=$(cat << COMMIT_EOF +${TAG} + +Built with: ${GO_VERSION} +Build date: ${BUILD_DATE} + +SHA256 checksums: +${CHECKSUMS_TEXT} +COMMIT_EOF +) + +git add BUILDNUM +git commit -m "$COMMIT_MSG" +print_ok "Commit created (BUILDNUM bumped to ${NEXT_BUILDNUM})" + +# --------------------------------------------------------------------------- +# 10. Create annotated tag pointing at source commit +# --------------------------------------------------------------------------- +echo "" +echo "=== Creating tag ===" + +TAG_MSG=$(cat << TAG_EOF +c65gm ${TAG} + +Built with: ${GO_VERSION} +Build date: ${BUILD_DATE} + +SHA256 checksums: +${CHECKSUMS_TEXT} + +To reproduce this build: + git checkout ${TAG} + go build -trimpath -ldflags="${LDFLAGS}" + +Then verify: + sha256sum c65gm +TAG_EOF +) + +git tag -a "$TAG" "$SOURCE_COMMIT" -m "$TAG_MSG" +print_ok "Tag ${TAG} created (pointing at ${SOURCE_COMMIT})" + +# --------------------------------------------------------------------------- +# 11. Summary +# --------------------------------------------------------------------------- +echo "" +echo "============================================" +echo " c65gm ${TAG} released!" +echo "" +echo " Source commit: ${SOURCE_COMMIT}" +echo " BUILDNUM: ${BUILDNUM} → ${NEXT_BUILDNUM}" +echo " Artifacts in: ${DIST}/" +echo " Go version: ${GO_VERSION}" +echo "" +echo " To publish:" +echo " git push --follow-tags origin" +echo "" +echo " Artifacts:" +ls -lh "${DIST}/" | sed 's/^/ /' +echo "============================================" +echo ""