diff --git a/.github/workflows/build_binaries.yml b/.github/workflows/build_binaries.yml deleted file mode 100644 index dbe667ff..00000000 --- a/.github/workflows/build_binaries.yml +++ /dev/null @@ -1,63 +0,0 @@ -name: Build and Commit Binaries - -on: - push: - branches: [ master ] - paths-ignore: # Prevents the workflow from running on binaries changes. - - 'bin/**' - -concurrency: - group: building - cancel-in-progress: true - -jobs: - - build: - runs-on: ubuntu-latest - timeout-minutes: 10 - - steps: - - # Initializes an SSH agent and loads the Deploy Key. - # All subsequent git operations (fetch/push) are authenticated via SSH. - # - # This step uses Deploy Key to bypass the master branch protection rule, - # which allows to push changes to the master branch only via pull request. - # - - name: Setup SSH - uses: webfactory/ssh-agent@v0.9.0 - with: - ssh-private-key: ${{ secrets.WORKFLOW_DEPLOY_KEY }} - - # Checkout the repository via SSH Deploy Key. - - name: Checkout Repository - uses: actions/checkout@v4 - with: - ssh-key: ${{ secrets.WORKFLOW_DEPLOY_KEY }} - persist-credentials: false - - - name: Set Up Go Environment - uses: actions/setup-go@v5 - with: - go-version-file: 'go.mod' - - - name: Build embed_code.go for Windows - run: GOOS=windows GOARCH=amd64 go build -trimpath -o bin/embed-code-windows.exe main.go - - - name: Build embed_code.go for Ubuntu - run: > - GOOS=linux GOARCH=amd64 go build -trimpath -o bin/embed-code-linux main.go - && chmod +x bin/embed-code-linux - - - name: Build embed_code.go for MacOS - run: > - GOOS=darwin GOARCH=amd64 go build -trimpath -o bin/embed-code-macos main.go - && chmod +x bin/embed-code-macos - - - name: Commit Built Files - uses: EndBug/add-and-commit@v9 - with: - add: './bin' - message: 'Update binaries.' - default_author: github_actions - push: true diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 39c2a6f5..8320c098 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Set Up Go Environment uses: actions/setup-go@v5 diff --git a/.github/workflows/release-binaries.yml b/.github/workflows/release-binaries.yml new file mode 100644 index 00000000..122550d3 --- /dev/null +++ b/.github/workflows/release-binaries.yml @@ -0,0 +1,88 @@ +# Releases binaries when application version changes. +name: Release Binaries + +on: + push: + branches: [ master ] + +permissions: + contents: write + +# Keep only the newest release run for the same ref. This avoids competing +# release attempts when several pushes land close together. +concurrency: + group: release-binaries-${{ github.ref }} + cancel-in-progress: true + +jobs: + + release: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout Repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + # Use the same Go version as the project, declared in go.mod. + - name: Set Up Go Environment + uses: actions/setup-go@v5 + with: + go-version-file: 'go.mod' + + # Reads the app version from the `VERSION` file. + # It must be a plain MAJOR.MINOR.PATCH value, such as 1.2.3. + - name: Read Version + id: version + shell: bash + run: | + version="$(< VERSION)" + if [[ ! "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Version must use MAJOR.MINOR.PATCH format, found: $version" >&2 + exit 1 + fi + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "tag=v$version" >> "$GITHUB_OUTPUT" + + # Compares the release version with current app version. + # If current app version already exist in release, emits a warning, but flow counts as passed. + - name: Check Release Version + id: release + shell: bash + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ steps.version.outputs.tag }} + run: | + if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then + echo "::warning title=Release already exists::Release $RELEASE_TAG already exists. Bump VERSION to publish a new release." + echo "publish=false" >> "$GITHUB_OUTPUT" + else + echo "publish=true" >> "$GITHUB_OUTPUT" + fi + + # Build binaries only for new releases. + - name: Build Binaries + if: steps.release.outputs.publish == 'true' + shell: bash + run: | + mkdir -p dist + GOOS=windows GOARCH=amd64 go build -trimpath -o dist/embed-code-windows.exe main.go + GOOS=linux GOARCH=amd64 go build -trimpath -o dist/embed-code-linux main.go + GOOS=darwin GOARCH=amd64 go build -trimpath -o dist/embed-code-macos main.go + chmod +x dist/embed-code-linux dist/embed-code-macos + + # Create the release for the current commit and attach all generated binaries. + - name: Publish GitHub Release + if: steps.release.outputs.publish == 'true' + shell: bash + env: + GH_TOKEN: ${{ github.token }} + RELEASE_TAG: ${{ steps.version.outputs.tag }} + run: | + release_notes="$(printf 'Embed Code %s\n\nThis release contains pre-built Embed Code binaries for macOS, Linux, and Windows.' "$RELEASE_TAG")" + gh release create "$RELEASE_TAG" dist/* \ + --target "$GITHUB_SHA" \ + --title "embed-code $RELEASE_TAG" \ + --notes "$release_notes" diff --git a/.gitignore b/.gitignore index a81fa457..a1f29d3f 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ coverage.out # Built 'main' application main +embed-code +embed-code.exe diff --git a/AGENTS.md b/AGENTS.md index 7e64594e..9f1e4795 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,7 +18,6 @@ agent operating policy and repository-wide rules. ## Repository Hygiene - Do not revert unrelated user changes. -- Do not edit generated binaries under `bin/` unless explicitly requested. - Do not add temporary repo files, local binaries, IDE metadata, coverage output, or build artifacts to the intended change set. - Keep changes narrowly scoped and make unrelated cleanup a separate task. diff --git a/PROJECT.md b/PROJECT.md index 7807e430..f2076c50 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -55,13 +55,9 @@ This repository is configured with these GitHub workflows: - `check`: runs linting, the normal Go test suite, and the showcase end-to-end tests across supported platforms. -- `build_binaries`: builds binaries on pushes to `master`. +- `release-binaries`: reads `VERSION`, builds Linux, macOS, and Windows + binaries, and creates the matching GitHub Release on pushes to `master`. -`build_binaries` uses a deploy key instead of the default GitHub Actions bot so -it can push through branch protection. If it must be rotated: - -1. Generate an SSH key pair for GitHub: - `ssh -i ~/.ssh/workflow_deploy_key -T git@github.com`. -2. Add `workflow_deploy_key.pub` as a GitHub deploy key with write access. -3. Add `workflow_deploy_key` as a repository secret named - `WORKFLOW_DEPLOY_KEY`. +The release tag is `v` from `VERSION`. When the release already exists, +the workflow emits a warning and finishes successfully without rebuilding or +uploading binaries. diff --git a/README.md b/README.md index 7e175572..831c3c84 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,25 @@ failures, and runnable examples. ## Run -Use a prebuilt binary from [GitHub Releases][releases]: +Download the asset for your platform from [GitHub Releases][releases]. + +On Linux, for example: ```bash -./bin/embed-code-linux -mode=check -config-path=showcase/embedding/embed-code.yml +./embed-code-linux -mode=check -config-path=showcase/embedding/embed-code.yml ``` +> It may be necessary to give the executable permission with `chmod +x` on Unix-like systems: +> ```bash +> chmod +x embed-code-linux +> ``` + +> Since binary file for macOS is not signed, it may be necessary +> to change its attributes to allow execution: +> ```bash +> xattr -d com.apple.quarantine embed-code-macos +> ``` + Or run it with Go: ```bash @@ -48,8 +61,7 @@ go build -trimpath -o embed-code main.go ``` This creates `embed-code` on Unix-like systems or `embed-code.exe` on Windows. -The `-trimpath` flag prevents local absolute paths from appearing in stack -traces. +The `-trimpath` flag prevents local absolute paths from appearing in stack traces. ## Development diff --git a/VERSION b/VERSION new file mode 100644 index 00000000..0495c4a8 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +1.2.3 diff --git a/bin/embed-code-linux b/bin/embed-code-linux deleted file mode 100755 index 51b0266e..00000000 Binary files a/bin/embed-code-linux and /dev/null differ diff --git a/bin/embed-code-macos b/bin/embed-code-macos deleted file mode 100755 index fa31506d..00000000 Binary files a/bin/embed-code-macos and /dev/null differ diff --git a/bin/embed-code-windows.exe b/bin/embed-code-windows.exe deleted file mode 100755 index dd5b1008..00000000 Binary files a/bin/embed-code-windows.exe and /dev/null differ diff --git a/main.go b/main.go index 7292053b..fec817ce 100644 --- a/main.go +++ b/main.go @@ -19,18 +19,23 @@ package main import ( - "embed-code/embed-code-go/cli" - "embed-code/embed-code-go/configuration" - "embed-code/embed-code-go/logging" + _ "embed" "errors" "fmt" "log/slog" "os" "strings" + + "embed-code/embed-code-go/cli" + "embed-code/embed-code-go/configuration" + "embed-code/embed-code-go/logging" ) -// Version of the embed-code application. -const Version = "1.2.2" +//go:embed VERSION +var versionFile string + +// Version is the embed-code application version embedded from the VERSION file. +var Version = strings.TrimSpace(versionFile) // The entry point for embed-code. //