Compare commits

...

22 Commits

Author SHA1 Message Date
387c2c40aa docs: add CLAUDE.md for project guidance
- Introduced CLAUDE.md to provide comprehensive guidance on the macOS Emacs building system, including project overview, common commands, architecture details, and testing instructions.
2026-01-14 18:56:25 +00:00
527f855fbf chore(deps): add macOS SDK version 26
- Added support for macOS SDK version 26 to the list of supported SDKs.
- Reformatted flake.nix with nixfmt.
2026-01-14 18:53:56 +00:00
aafac57b1d chore(deps): update flake.lock and flake.pkgs for dependency versions
- Updated nixpkgs revision and narHash in flake.lock.
- Upgraded expat to version 2.7.3 and openssl to version 3.4.3 in flake.pkgs.
- Modified flake-package-versions command in Makefile for improved output.
2026-01-14 18:50:55 +00:00
jimehbot[bot]
4367d6b80e chore(master): release 0.6.63 (#149)
Co-authored-by: jimehbot[bot] <132453784+jimehbot[bot]@users.noreply.github.com>
2025-09-14 21:01:51 +01:00
b5ed3887c1 feat(icon): add support for Tahoe icons with --tahoe-icon-uri and --tahoe-icon-name options 2025-09-14 20:58:12 +01:00
607076a91b feat(icon): add support for custom application icons via --icon-uri option 2025-09-14 20:57:17 +01:00
1cda2cc3ff style(lint): resolve Naming/PredicateName rubocop complaint 2025-09-14 20:55:50 +01:00
jimehbot[bot]
39a4868313 chore(master): release 0.6.62 (#148)
Co-authored-by: jimehbot[bot] <132453784+jimehbot[bot]@users.noreply.github.com>
2025-09-14 12:25:30 +01:00
2086a773ae feat(deps/nix): upgrade build-time dependencies
In particular, this brings tree-sitter up to v0.25.3, enabling
tree-sitter ABI v15.
2025-09-14 12:23:27 +01:00
jimehbot[bot]
68ecce6158 chore(master): release 0.6.61 (#145)
Co-authored-by: jimehbot[bot] <132453784+jimehbot[bot]@users.noreply.github.com>
2025-08-13 01:40:23 +01:00
7aa4058128 fix(emacs-31-builds): disable fix-window-role patch after it was merged upstream 2025-08-13 01:38:46 +01:00
jimehbot[bot]
67ad73e500 chore(master): release 0.6.60 (#141)
Co-authored-by: jimehbot[bot] <132453784+jimehbot[bot]@users.noreply.github.com>
2025-06-27 22:42:03 +01:00
8ac1f946dd fix(builder/cask): correctly resolve version with build variant to release name (#140) 2025-06-27 22:40:57 +01:00
jimehbot[bot]
cc38319b40 chore(master): release 0.6.59 (#139)
Co-authored-by: jimehbot[bot] <132453784+jimehbot[bot]@users.noreply.github.com>
2025-06-27 15:25:59 +01:00
e8885400e6 fix(builder/plan): append test build name to release name (#138) 2025-06-27 15:24:55 +01:00
github-actions[bot]
00015d861f chore(master): release 0.6.58 (#135)
Co-authored-by: jimehbot[bot] <132453784+jimehbot[bot]@users.noreply.github.com>
2025-06-27 11:41:15 +01:00
ca8b874be2 feat(builder/plan): add build variant flag (#137) 2025-06-27 11:39:52 +01:00
5d36f02cca ci(release-please): use custom bot account to publish release PRs 2025-06-27 11:32:25 +01:00
5c37e8b0a2 ci(permissions): set correct permissions for release-please job 2025-06-27 11:27:56 +01:00
28ff28b29a chore(ci/deps): upgrade CI dependencies and fix linting issues (#136) 2025-06-27 11:25:16 +01:00
3cf1977def fix(deps): work around duplicate RPATHs in libgccjit from Nix (#134) 2025-06-27 11:05:46 +01:00
bc2a45767e chore(deps): use HTTPS for Gemfile source directive 2025-06-26 20:02:08 +01:00
20 changed files with 829 additions and 246 deletions

View File

@@ -1,3 +1,3 @@
{ {
".": "0.6.57" ".": "0.6.63"
} }

View File

@@ -1,5 +1,6 @@
{ {
"bootstrap-sha": "3d6c7fff64bda8ba0dbea181c9f94fb9716dd188", "bootstrap-sha": "3d6c7fff64bda8ba0dbea181c9f94fb9716dd188",
"always-update": true,
"packages": { "packages": {
".": { ".": {
"release-type": "simple", "release-type": "simple",

View File

@@ -1,6 +1,8 @@
--- ---
name: CI name: CI
on: [push] on: [push]
permissions:
contents: read
jobs: jobs:
lint: lint:
@@ -10,11 +12,11 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: "1.23" go-version-file: go.mod
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v6 uses: golangci/golangci-lint-action@v8
with: with:
version: v1.61 version: v2.1
env: env:
VERBOSE: "true" VERBOSE: "true"
@@ -25,7 +27,7 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: "1.23" go-version-file: go.mod
- name: Check if mods are tidy - name: Check if mods are tidy
run: make check-tidy run: make check-tidy
@@ -36,14 +38,21 @@ jobs:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-go@v5 - uses: actions/setup-go@v5
with: with:
go-version: "1.23" go-version-file: go.mod
- name: Run tests - name: Run tests
run: make test run: make test
env: env:
VERBOSE: "true" VERBOSE: "true"
release-please: release-please:
name: Release Please
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.ref_name == 'main' || github.ref_name == 'master' if: ${{ github.ref_name == 'main' || github.ref_name == 'master' }}
permissions:
contents: write
pull-requests: write
steps: steps:
- uses: jimeh/release-please-manifest-action@v1 - uses: jimeh/release-please-manifest-action@v2
with:
app-id: ${{ secrets.RELEASE_BOT_APP_ID }}
private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}

View File

@@ -1,25 +1,9 @@
linters-settings: version: "2"
funlen: run:
lines: 100 modules-download-mode: readonly
statements: 150 allow-parallel-runners: true
goconst:
min-occurrences: 5
gocyclo:
min-complexity: 20
govet:
enable-all: true
disable:
- fieldalignment
lll:
line-length: 80
tab-width: 4
maligned:
suggest-new: true
misspell:
locale: US
linters: linters:
disable-all: true default: none
enable: enable:
- bodyclose - bodyclose
- copyloopvar - copyloopvar
@@ -30,12 +14,8 @@ linters:
- goconst - goconst
- gocritic - gocritic
- gocyclo - gocyclo
- gofumpt
- goimports
- goprintffuncname
- goprintffuncname - goprintffuncname
- gosec - gosec
- gosimple
- govet - govet
- ineffassign - ineffassign
- lll - lll
@@ -47,36 +27,69 @@ linters:
- revive - revive
- sqlclosecheck - sqlclosecheck
- staticcheck - staticcheck
- typecheck
- unconvert - unconvert
- unused - unused
- whitespace - whitespace
settings:
issues: funlen:
exclude: lines: 100
- Using the variable on range scope `tt` in function literal statements: 150
- Using the variable on range scope `tc` in function literal goconst:
exclude-rules: min-occurrences: 5
- path: "_test\\.go" gocyclo:
linters: min-complexity: 20
- funlen govet:
- dupl disable:
- goconst - fieldalignment
- source: "^//go:generate " enable-all: true
linters: lll:
- lll line-length: 80
- source: "`json:" tab-width: 4
linters: misspell:
- lll locale: US
- source: "`yaml:" exclusions:
linters: generated: lax
- lll presets:
exclude-dirs: - comments
- builds - common-false-positives
- sources - legacy
- tarballs - std-error-handling
rules:
run: - linters:
timeout: 2m - dupl
allow-parallel-runners: true - funlen
modules-download-mode: readonly - goconst
path: _test\.go
- linters:
- lll
source: "^//go:generate "
- linters:
- lll
source: "`json:"
- linters:
- lll
source: "`yaml:"
- path: (.+)\.go$
text: Using the variable on range scope `tt` in function literal
- path: (.+)\.go$
text: Using the variable on range scope `tc` in function literal
paths:
- builds
- sources
- tarballs
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofumpt
- goimports
exclusions:
generated: lax
paths:
- builds
- sources
- tarballs
- third_party$
- builtin$
- examples$

View File

@@ -1,5 +1,53 @@
# Changelog # Changelog
## [0.6.63](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.62...v0.6.63) (2025-09-14)
### Features
* **icon:** add support for custom application icons via --icon-uri option ([607076a](https://github.com/jimeh/build-emacs-for-macos/commit/607076a91bf0f227d16c9404f01a64144290685a))
* **icon:** add support for Tahoe icons with --tahoe-icon-uri and --tahoe-icon-name options ([b5ed388](https://github.com/jimeh/build-emacs-for-macos/commit/b5ed3887c172540de4a6190072b1e15f5d5efe2c))
## [0.6.62](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.61...v0.6.62) (2025-09-14)
### Features
* **deps/nix:** upgrade build-time dependencies ([2086a77](https://github.com/jimeh/build-emacs-for-macos/commit/2086a773ae6bf5f4c2e6863d1b06a85acc082e6d))
## [0.6.61](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.60...v0.6.61) (2025-08-13)
### Bug Fixes
* **emacs-31-builds:** disable fix-window-role patch after it was merged upstream ([7aa4058](https://github.com/jimeh/build-emacs-for-macos/commit/7aa405812802abbd2b78d8c98aee7fca23a2eab2))
## [0.6.60](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.59...v0.6.60) (2025-06-27)
### Bug Fixes
* **builder/cask:** correctly resolve version with build variant to release name ([#140](https://github.com/jimeh/build-emacs-for-macos/issues/140)) ([8ac1f94](https://github.com/jimeh/build-emacs-for-macos/commit/8ac1f946dde2342fa82aff7f90d2126bdd1f0057))
## [0.6.59](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.58...v0.6.59) (2025-06-27)
### Bug Fixes
* **builder/plan:** append test build name to release name ([#138](https://github.com/jimeh/build-emacs-for-macos/issues/138)) ([e888540](https://github.com/jimeh/build-emacs-for-macos/commit/e8885400e66bdb9304f99d9b072aa4dec4e83f4b))
## [0.6.58](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.57...v0.6.58) (2025-06-27)
### Features
* **builder/plan:** add build variant flag ([#137](https://github.com/jimeh/build-emacs-for-macos/issues/137)) ([ca8b874](https://github.com/jimeh/build-emacs-for-macos/commit/ca8b874be2c8c52cd7dcb05ff1348469e16c74ba))
### Bug Fixes
* **deps:** work around duplicate RPATHs in libgccjit from Nix ([#134](https://github.com/jimeh/build-emacs-for-macos/issues/134)) ([3cf1977](https://github.com/jimeh/build-emacs-for-macos/commit/3cf1977def02d1f3732b1051bc07a923557f9edd))
## [0.6.57](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.56...v0.6.57) (2024-12-07) ## [0.6.57](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.56...v0.6.57) (2024-12-07)

80
CLAUDE.md Normal file
View File

@@ -0,0 +1,80 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with
code in this repository.
## Project Overview
macOS Emacs building system with Ruby and Go components:
- **Ruby script** (`build-emacs-for-macos`): Main build script for creating
self-contained Emacs.app bundles
- **Go CLI tool** (`emacs-builder`): Packaging, signing, notarization, and
release management
- **Dual dependency management**: Nix (preferred) or Homebrew
## Common Commands
```bash
# Environment setup (Nix preferred)
nix develop # Default macOS 11 SDK
nix develop .#macos{11-15,26} # Target specific SDK version
# Go development
make build # Build emacs-builder CLI
make test # Run tests with race detection
make lint # Run golangci-lint
make format # Format with gofumpt
# Ruby development
bundle exec rubocop # Lint (with development group)
# Build Emacs
./build-emacs-for-macos # Build from master
./build-emacs-for-macos emacs-29.4
```
## Architecture
### Ruby Build Script (`build-emacs-for-macos`)
Single-file Ruby script (~2500 lines) that:
- Downloads source tarballs from emacs-mirror/emacs on GitHub
- Configures and compiles Emacs with native-comp, tree-sitter support
- Creates self-contained .app bundles by embedding/relinking dependencies
- Uses `ruby-macho` gem for Mach-O binary manipulation (RPATH handling)
### Go CLI (`cmd/emacs-builder/`)
Uses `urfave/cli/v2` framework. Key packages in `pkg/`:
- `cli/`: Commands (plan, sign, sign-files, notarize, package, release, cask)
- `sign/`: macOS code signing via `codesign`
- `notarize/`: Apple notarization workflow via `notarytool`
- `release/`: GitHub release management
- `dmgbuild/`: DMG creation using Python dmgbuild
- `plan/`: Build plan JSON parsing and management
### Nix Environment (`flake.nix`)
- Multi-SDK support: macOS 11-15, 26 via `.#macos{11,12,13,14,15,26}`
- Excludes ncurses intentionally (links against system version for TUI)
- Sets `MACOSX_DEPLOYMENT_TARGET`, `DEVELOPER_DIR`, `NIX_LIBGCCJIT_*`
## Testing
```bash
make test # All Go tests
go test ./pkg/release/... # Single package
go test -run TestName ./pkg/... # Single test
```
Tests use `_test.go` suffix alongside source files.
## Working Directories
- `sources/`: Downloaded/extracted Emacs source (gitignored)
- `builds/`: Build outputs and .app bundles (gitignored)
- `patches/`: Emacs patches applied during build
- `bin/`: Built Go binaries (gitignored)

View File

@@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
source 'http://rubygems.org/' source 'https://rubygems.org/'
gem 'ruby-macho' gem 'ruby-macho'

View File

@@ -1,5 +1,5 @@
GEM GEM
remote: http://rubygems.org/ remote: https://rubygems.org/
specs: specs:
ast (2.4.2) ast (2.4.2)
json (2.8.2) json (2.8.2)

View File

@@ -71,7 +71,7 @@ $(TOOLDIR)/$(1): Makefile
endef endef
$(eval $(call tool,gofumpt,mvdan.cc/gofumpt@latest)) $(eval $(call tool,gofumpt,mvdan.cc/gofumpt@latest))
$(eval $(call tool,golangci-lint,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61)) $(eval $(call tool,golangci-lint,github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1))
$(eval $(call tool,gomod,github.com/Helcaraxan/gomod@latest)) $(eval $(call tool,gomod,github.com/Helcaraxan/gomod@latest))
.PHONY: tools .PHONY: tools
@@ -141,10 +141,10 @@ nix-flake-update:
.SILENT: flake-package-versions .SILENT: flake-package-versions
flake-package-versions: flake-package-versions:
nix develop --command -- bash -c \ nix develop --command bash -c \
'nix derivation show \ 'nix derivation show \
$$(echo $$PATH | tr ":" "\n" | grep "/nix/store" | sort -u) \ $$(echo $$PATH | tr ":" "\n" | grep "/nix/store" | sort -u) \
| jq -r ".[].name" | sort -u' | jq -r ".derivations[].name" | sort -u'
flake.pkgs: flake.nix flake.lock flake.pkgs: flake.nix flake.lock
$(MAKE) flake-package-versions > "$@" $(MAKE) flake-package-versions > "$@"

View File

@@ -141,7 +141,7 @@ Options:
--[no-]relink-eln-files Enable/disable re-linking shared libraries in bundled *.eln files (default: enabled) --[no-]relink-eln-files Enable/disable re-linking shared libraries in bundled *.eln files (default: enabled)
--[no-]rsvg Enable/disable SVG image support via librsvg (default: enabled) --[no-]rsvg Enable/disable SVG image support via librsvg (default: enabled)
--[no-]dbus Enable/disable dbus support (default: enabled) --[no-]dbus Enable/disable dbus support (default: enabled)
--[no-]alpha-background Enable/disable experimental alpha-background patch when building Emacs 30.x - 31.x (default: disabled) --alpha-background Apply experimental alpha-background patch when building Emacs 30.x - 31.x (default: disabled)
--no-frame-refocus Apply no-frame-refocus patch when building Emacs 27.x - 31.x (default: disabled) --no-frame-refocus Apply no-frame-refocus patch when building Emacs 27.x - 31.x (default: disabled)
--no-titlebar Apply no-titlebar patch when building Emacs 27.x - 28.x (default: disabled) --no-titlebar Apply no-titlebar patch when building Emacs 27.x - 28.x (default: disabled)
--[no-]xwidgets Enable/disable XWidgets when building Emacs 27.x (default: disabled) --[no-]xwidgets Enable/disable XWidgets when building Emacs 27.x (default: disabled)
@@ -155,12 +155,16 @@ Options:
-o, --output DIR Output directory for finished builds (default: <work-dir>/builds) -o, --output DIR Output directory for finished builds (default: <work-dir>/builds)
--build-name NAME Override generated build name --build-name NAME Override generated build name
--dist-include x,y,z List of extra files to copy from Emacs source into build folder/archive (default: COPYING) --dist-include x,y,z List of extra files to copy from Emacs source into build folder/archive (default: COPYING)
--icon-uri URI Local path or URL to a .icns file to replace the default app icon
--tahoe-icon-uri URI Local path or URL to an Assets.car file for macOS 26 icons. Requires --tahoe-icon-name.
--tahoe-icon-name NAME Name of the icon in Assets.car to set as CFBundleIconName
--[no-]self-sign Enable/disable self-signing of Emacs.app (default: enabled) --[no-]self-sign Enable/disable self-signing of Emacs.app (default: enabled)
--[no-]archive Enable/disable creating *.tbz archive (default: enabled) --[no-]archive Enable/disable creating *.tbz archive (default: enabled)
--[no-]archive-keep-build-dir --[no-]archive-keep-build-dir
Enable/disable keeping source folder for archive (default: disabled) Enable/disable keeping source folder for archive (default: disabled)
--log-level LEVEL Build script log level (default: info) --log-level LEVEL Build script log level (default: info)
--plan FILE Follow given plan file, instead of using given git ref/sha --plan FILE Follow given plan file, instead of using given git ref/sha
--clean-macho-binary FILE Tool to clean duplicate RPATHs from given Mach-O binary.
``` ```
Resulting applications are saved to the `builds` directory in a bzip2 compressed Resulting applications are saved to the `builds` directory in a bzip2 compressed

View File

@@ -12,6 +12,7 @@ require 'net/http'
require 'open3' require 'open3'
require 'optparse' require 'optparse'
require 'pathname' require 'pathname'
require 'set'
require 'time' require 'time'
require 'tmpdir' require 'tmpdir'
require 'uri' require 'uri'
@@ -228,6 +229,14 @@ class Build
handle_native_lisp(app) handle_native_lisp(app)
if options[:icon_uri] || options[:tahoe_icon_uri] || options[:tahoe_icon_name]
IconEmbedder.new(
app,
icon_uri: options[:icon_uri],
tahoe_icon_uri: options[:tahoe_icon_uri],
tahoe_icon_name: options[:tahoe_icon_name]
).embed
end
CLIHelperEmbedder.new(app).embed CLIHelperEmbedder.new(app).embed
CSourcesEmbedder.new(app, @source_dir).embed CSourcesEmbedder.new(app, @source_dir).embed
LibEmbedder.new( LibEmbedder.new(
@@ -506,7 +515,7 @@ class Build
def env_CFLAGS def env_CFLAGS
return @env_CFLAGS if @env_CFLAGS return @env_CFLAGS if @env_CFLAGS
env = [] env = ENV.fetch('CFLAGS', nil)&.split || []
env << '-O2' env << '-O2'
@@ -532,13 +541,28 @@ class Build
env += ENV['NIX_CFLAGS_COMPILE'].split env += ENV['NIX_CFLAGS_COMPILE'].split
end end
@env_CFLAGS = env # Group "-isystem <path>" flags together as a single flag. This allows us to
# de-duplicate CFLAGS from NIX_CFLAGS_COMPILE.
new_env = []
isystem_flag = false
env.each do |flag|
if flag.strip == '-isystem'
isystem_flag = true
elsif isystem_flag
new_env << "-isystem #{flag}"
isystem_flag = false
else
new_env << flag
end
end
@env_CFLAGS = new_env.compact.reject(&:empty?).uniq
end end
def env_LDFLAGS def env_LDFLAGS
return @env_LDFLAGS if @env_LDFLAGS return @env_LDFLAGS if @env_LDFLAGS
env = [] env = ENV.fetch('LDFLAGS', nil)&.split || []
# Ensure library re-linking and code signing will work after building. # Ensure library re-linking and code signing will work after building.
env << '-Wl,-headerpad_max_install_names' env << '-Wl,-headerpad_max_install_names'
@@ -555,13 +579,13 @@ class Build
env += ENV['NIX_LDFLAGS'].split if use_nix? && ENV['NIX_LDFLAGS'] env += ENV['NIX_LDFLAGS'].split if use_nix? && ENV['NIX_LDFLAGS']
@env_LDFLAGS = env @env_LDFLAGS = env.compact.reject(&:empty?).uniq
end end
def env_LIBRARY_PATH def env_LIBRARY_PATH
return @env_LIBRARY_PATH if @env_LIBRARY_PATH return @env_LIBRARY_PATH if @env_LIBRARY_PATH
env = [] env = ENV.fetch('LIBRARY_PATH', nil)&.split || []
if options[:native_comp] if options[:native_comp]
env += [ env += [
@@ -573,37 +597,45 @@ class Build
env << '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib' env << '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib'
@env_LIBRARY_PATH = env @env_LIBRARY_PATH = env.compact.reject(&:empty?).uniq
end end
def env_PKG_CONFIG_PATH def env_PKG_CONFIG_PATH
return [] if use_nix? env = ENV.fetch('PKG_CONFIG_PATH', nil)&.split || []
@env_PKG_CONFIG_PATH ||= [ return env if use_nix?
File.join(brew_dir, 'lib/pkgconfig'),
File.join(brew_dir, 'share/pkgconfig'), @env_PKG_CONFIG_PATH = (
File.join(brew_dir, 'opt/expat/lib/pkgconfig'), [
File.join(brew_dir, 'opt/libxml2/lib/pkgconfig'), File.join(brew_dir, 'lib/pkgconfig'),
File.join(brew_dir, 'opt/ncurses/lib/pkgconfig'), File.join(brew_dir, 'share/pkgconfig'),
File.join(brew_dir, 'opt/zlib/lib/pkgconfig'), File.join(brew_dir, 'opt/expat/lib/pkgconfig'),
File.join( File.join(brew_dir, 'opt/libxml2/lib/pkgconfig'),
brew_dir, File.join(brew_dir, 'opt/ncurses/lib/pkgconfig'),
'Homebrew/Library/Homebrew/os/mac/pkgconfig', File.join(brew_dir, 'opt/zlib/lib/pkgconfig'),
OS.version.to_s File.join(
) brew_dir,
] 'Homebrew/Library/Homebrew/os/mac/pkgconfig',
OS.version.to_s
)
] + env
).compact.reject(&:empty?).uniq
end end
def env_PATH def env_PATH
return [] if use_nix? env = ENV.fetch('PATH', nil)&.split || []
@env_PATH ||= [ return env if use_nix?
File.join(brew_dir, 'opt/make/libexec/gnubin'),
File.join(brew_dir, 'opt/coreutils/libexec/gnubin'), @env_PATH = (
File.join(brew_dir, 'opt/gnu-sed/libexec/gnubin'), [
File.join(brew_dir, 'bin'), File.join(brew_dir, 'opt/make/libexec/gnubin'),
File.join(brew_dir, 'opt/texinfo/bin') File.join(brew_dir, 'opt/coreutils/libexec/gnubin'),
] File.join(brew_dir, 'opt/gnu-sed/libexec/gnubin'),
File.join(brew_dir, 'bin'),
File.join(brew_dir, 'opt/texinfo/bin')
] + env
).compact.reject(&:empty?).uniq
end end
# rubocop:enable Naming/MethodName,Naming/VariableName # rubocop:enable Naming/MethodName,Naming/VariableName
@@ -612,22 +644,14 @@ class Build
env = { env = {
'CC' => use_nix? ? 'clang' : '/usr/bin/clang', 'CC' => use_nix? ? 'clang' : '/usr/bin/clang',
'PATH' => [ 'PATH' => env_PATH.join(':'),
env_PATH, ENV.fetch('PATH', nil) 'PKG_CONFIG_PATH' => env_PKG_CONFIG_PATH.join(':')
].flatten.compact.reject(&:empty?).join(':'),
'PKG_CONFIG_PATH' => [
env_PKG_CONFIG_PATH,
ENV.fetch('PKG_CONFIG_PATH', nil)
].flatten.compact.reject(&:empty?).join(':')
} }
if options[:native_comp] if options[:native_comp]
env['CFLAGS'] = [env_CFLAGS, ENV.fetch('CFLAGS', nil)] env['CFLAGS'] = env_CFLAGS.join(' ')
.flatten.compact.reject(&:empty?).join(' ') env['LDFLAGS'] = env_LDFLAGS.join(' ')
env['LDFLAGS'] = [env_LDFLAGS, ENV.fetch('LDFLAGS', nil)] env['LIBRARY_PATH'] = env_LIBRARY_PATH.join(':')
.flatten.compact.reject(&:empty?).join(' ')
env['LIBRARY_PATH'] = [env_LIBRARY_PATH, ENV.fetch('LIBRARY_PATH', nil)]
.flatten.compact.reject(&:empty?).join(':')
end end
@compile_env = env @compile_env = env
@@ -1057,7 +1081,7 @@ class Build
# Enabled by default patches. # Enabled by default patches.
if (26..31).include?(effective_version) if (26..30).include?(effective_version)
p << { p << {
url: url:
'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \ 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
@@ -1065,6 +1089,19 @@ class Build
} }
end end
# The fix-window-role patch was merged into Emacs 31 on 2025-07-31 with
# commit 6e1054a40bf6df1429a2b16fdd0d7652dae4d537. Hence builds for commits
# before then need the patch from the last commit in emacs-plus before it
# was removed.
if effective_version == 31 && meta[:date] < Time.parse('2025-07-31')
p << {
url:
'https://github.com/d12frosted/homebrew-emacs-plus/raw/' \
'3e95d573d5f13aba7808193b66312b38a7c66851/' \
'patches/emacs-31/fix-window-role.patch'
}
end
if (27..31).include?(effective_version) if (27..31).include?(effective_version)
p << { p << {
url: url:
@@ -1384,6 +1421,98 @@ class CSourcesEmbedder < AbstractEmbedder
end end
end end
class IconEmbedder < AbstractEmbedder
include Helpers
def initialize(app, icon_uri: nil, tahoe_icon_uri: nil, tahoe_icon_name: nil)
super(app)
@icon_uri = icon_uri
@tahoe_icon_uri = tahoe_icon_uri
@tahoe_icon_name = tahoe_icon_name
end
def embed
handle_icns if present?(@icon_uri)
handle_tahoe if present?(@tahoe_icon_uri)
ensure
cleanup_tmpdir
end
private
def present?(val)
!val.nil? && !val.strip.empty?
end
def handle_icns
source = resolve_source(@icon_uri, '.icns', 'icon.icns')
target = File.join(resources_dir, 'Emacs.icns')
info 'Replacing application icon (Emacs.icns)...'
run_cmd('cp', '-pRL', source, target)
source
end
def handle_tahoe
fatal '--tahoe-icon-name is required with --tahoe-icon-uri' \
unless present?(@tahoe_icon_name)
source = resolve_source(@tahoe_icon_uri, '.car', 'Assets.car')
target = File.join(resources_dir, 'Assets.car')
info 'Placing Tahoe Assets.car into Resources...'
run_cmd('cp', '-pRL', source, target)
set_cf_bundle_icon_name(@tahoe_icon_name)
source
end
def set_cf_bundle_icon_name(name)
info 'Setting CFBundleIconName in Info.plist...'
info_plist = File.join(app, 'Contents', 'Info.plist')
fatal "Info.plist not found: #{info_plist}" unless File.exist?(info_plist)
# Use plutil which adds/replaces the key as needed
run_cmd(
'plutil', '-replace', 'CFBundleIconName', '-string',
name, info_plist
)
end
def resolve_source(uri, expected_ext, download_name)
file_path = if valid_url?(uri)
download_file(uri, download_name)
else
local = File.expand_path(uri)
unless File.exist?(local)
fatal "File does not exist: #{local}"
end
local
end
ext = File.extname(file_path).downcase
fatal "Unexpected file type: #{ext} (expected #{expected_ext})" \
unless ext == expected_ext
file_path
end
def tmpdir
@tmpdir ||= Dir.mktmpdir(%w[emacs-assets .tmp])
end
def download_file(url, name)
path = File.join(tmpdir, name)
info "Downloading asset from: #{url}"
run_cmd('curl', '-L#', url, '-o', path)
path
end
def cleanup_tmpdir
return unless @tmpdir
FileUtils.rm_rf(@tmpdir)
end
end
class LibEmbedder < AbstractEmbedder class LibEmbedder < AbstractEmbedder
attr_reader :lib_sources attr_reader :lib_sources
attr_reader :extra_libs attr_reader :extra_libs
@@ -1701,6 +1830,7 @@ end
class GccInfo class GccInfo
include Output include Output
include System
def initialize(use_nix: false) def initialize(use_nix: false)
@use_nix = use_nix @use_nix = use_nix
@@ -1823,7 +1953,7 @@ class GccInfo
Dir[ Dir[
File.join(libgccjit_root_dir, 'lib/gcc/*/libgccjit*.dylib'), File.join(libgccjit_root_dir, 'lib/gcc/*/libgccjit*.dylib'),
File.join(libgccjit_root_dir, 'lib/gcc/*/libgccjit.so*'), File.join(libgccjit_root_dir, 'lib/gcc/*/libgccjit.so*'),
] ]
.map { |path| File.dirname(path) } .map { |path| File.dirname(path) }
.select { |path| File.basename(path).match(/^\d+$/) } .select { |path| File.basename(path).match(/^\d+$/) }
.max_by { |path| File.basename(path).to_i } .max_by { |path| File.basename(path).to_i }
@@ -1840,10 +1970,15 @@ class GccInfo
'brew reinstall libgccjit' 'brew reinstall libgccjit'
end end
# No need to verify gcc vs libgccjit for Nix, as we can pull everything we if use_nix?
# need from the libgccjit package. On homebrew we need to pull parts from Dir[File.join(libgccjit_lib_dir, 'libgccjit*.dylib')]
# gcc and parts from libgccjit, hence we need to ensure versions match. .each { |path| clean_macho_binary(path) }
return if use_nix?
# No need to verify gcc vs libgccjit for Nix, as we can pull everything we
# need from the libgccjit package. On homebrew we need to pull parts from
# gcc and parts from libgccjit, hence we need to ensure versions match.
return
end
return if major_version == libgccjit_major_version return if major_version == libgccjit_major_version
@@ -1868,6 +2003,190 @@ class GccInfo
def relative_path(base, path) def relative_path(base, path)
Pathname.new(path).relative_path_from(Pathname.new(base)).to_s Pathname.new(path).relative_path_from(Pathname.new(base)).to_s
end end
def clean_macho_binary(path)
debug "Checking for duplicate RPATHs in #{path}"
macho_cleaner = MachOCleaner.new(path)
return unless macho_cleaner.duplicate_rpaths?
begin
info "Removing duplicate RPATHs from #{path}"
macho_cleaner.clean!
debug 'Cleaned duplicate RPATHs successfully!'
rescue MachOCleaner::PermissionError => e
warn "Could not remove duplicate RPATHs from #{path}: #{e.message}"
if ENV['USER'] == 'root'
fatal "Could not remove duplicate RPATHs from #{path}: #{e.message}"
else
warn '================================================================='
warn "Attempting to clean duplicate RPATHs from #{path} as root"
warn '================================================================='
run_cmd('sudo', $PROGRAM_NAME, '--clean-macho-binary', path)
end
end
end
end
# MachOCleaner is a class that cleans up a Mach-O file by removing all duplicate
# RPATH load commands. This ensures compatibility with macOS 15.4 and later,
# which refuses to load binaries and shared libraries with duplicate RPATHs.
class MachOCleaner
include Output
include System
class PermissionError < StandardError
def initialize(file, message = nil)
@file = file
super(message || "Insufficient permissions to modify #{file}")
end
attr_reader :file
end
attr_reader :file
def initialize(file_path, backup: true)
@file = file_path
@backup = backup
validate_file!
end
def backup?
@backup
end
# Main cleaning method - removes duplicate RPATH commands
def clean!
duplicate_paths = find_duplicate_rpaths(macho_object)
return if duplicate_paths.empty?
backup_file! if backup?
while_writable(@file) do
duplicate_paths.each do |rpath|
remove_rpath_with_install_name_tool!(rpath)
end
end
end
# Check if file has duplicate RPATH commands
def duplicate_rpaths?
count_duplicate_rpaths(macho_object).positive?
end
# Return total number of RPATH commands
def rpath_count
count_rpaths(macho_object)
end
# Return number of duplicate RPATH commands
def duplicate_rpath_count
count_duplicate_rpaths(macho_object)
end
private
# Validate that the file exists and is readable
def validate_file!
fatal "File does not exist: #{@file}" unless File.exist?(@file)
return if File.readable?(@file)
fatal "File is not readable: #{@file}"
end
# Load and memoize the Mach-O object
def macho_object
return @macho_object if @macho_object
begin
@macho_object = MachO.open(@file)
rescue MachO::MachOError => e
fatal "Not a valid Mach-O file: #{@file} (#{e.message})"
end
unless @macho_object.respond_to?(:rpaths)
fatal "Unsupported Mach-O file type: #{@file}"
end
@macho_object
end
def backup_file!
backup_file = "#{@file}.bak"
if File.exist?(backup_file)
debug "Backup file already exists: #{backup_file}"
return
end
FileUtils.cp(@file, backup_file)
debug "Backed up #{@file} to #{backup_file}"
rescue Errno::EPERM, Errno::EACCES => e
raise PermissionError.new(
backup_file, "Cannot create backup file: #{e.message}"
)
end
# Temporarily make file writable, execute block, then restore permissions
def while_writable(file)
# Check if file is already writable to avoid unnecessary permission changes
if File.writable?(file)
yield
return
end
original_mode = File.stat(file).mode
begin
File.chmod(0o755, file)
rescue Errno::EPERM, Errno::EACCES => e
raise PermissionError.new(
file, "Cannot change file permissions: #{e.message}"
)
end
yield
ensure
if File.exist?(file) && original_mode
begin
File.chmod(original_mode, file)
rescue Errno::EPERM, Errno::EACCES
# Log warning but don't fail - file was already modified successfully
warn "Warning: Could not restore original permissions for #{file}"
end
end
end
# Find duplicate RPATH commands in a Mach-O file
def find_duplicate_rpaths(macho_file)
seen = Set.new
duplicates = []
macho_file.rpaths.each do |rpath|
if seen.include?(rpath)
duplicates << rpath
else
seen.add(rpath)
end
end
duplicates
end
# Remove an RPATH using install_name_tool
def remove_rpath_with_install_name_tool!(rpath)
run_cmd('install_name_tool', '-delete_rpath', rpath, @file)
end
# Count total RPATH commands in a Mach-O file
def count_rpaths(macho_file)
macho_file.rpaths.size
end
# Count duplicate RPATH commands in a Mach-O file
def count_duplicate_rpaths(macho_file)
find_duplicate_rpaths(macho_file).size
end
end end
class CLIOptions class CLIOptions
@@ -1882,6 +2201,14 @@ class CLIOptions
def parse!(args) def parse!(args)
parser.parse!(args) parser.parse!(args)
if options[:tahoe_icon_uri] &&
(
options[:tahoe_icon_name].nil? ||
options[:tahoe_icon_name].strip.empty?
)
fatal '--tahoe-icon-name is required when --tahoe-icon-uri is specified'
end
rescue OptionParser::InvalidOption => e rescue OptionParser::InvalidOption => e
fatal e.message fatal e.message
end end
@@ -1908,11 +2235,15 @@ class CLIOptions
github_src_repo: nil, github_src_repo: nil,
github_auth: true, github_auth: true,
dist_include: ['COPYING', 'configure_output.txt'], dist_include: ['COPYING', 'configure_output.txt'],
icon_uri: nil,
tahoe_icon_uri: nil,
tahoe_icon_name: nil,
self_sign: true, self_sign: true,
archive: true, archive: true,
archive_keep: false, archive_keep: false,
patches: [], patches: [],
log_level: 'info' log_level: 'info',
clean_macho_binary: nil
} }
end end
@@ -2103,6 +2434,22 @@ class CLIOptions
'folder/archive (default: COPYING)' 'folder/archive (default: COPYING)'
) { |v| options[:dist_include] = v } ) { |v| options[:dist_include] = v }
opts.on(
'--icon-uri URI',
'Local path or URL to a .icns file to replace the default app icon'
) { |v| options[:icon_uri] = v }
opts.on(
'--tahoe-icon-uri URI',
'Local path or URL to an Assets.car file for macOS 26 icons. ' \
'Requires --tahoe-icon-name.'
) { |v| options[:tahoe_icon_uri] = v }
opts.on(
'--tahoe-icon-name NAME',
'Name of the icon in Assets.car to set as CFBundleIconName'
) { |v| options[:tahoe_icon_name] = v }
opts.on( opts.on(
'--[no-]self-sign', '--[no-]self-sign',
'Enable/disable self-signing of Emacs.app (default: enabled)' 'Enable/disable self-signing of Emacs.app (default: enabled)'
@@ -2128,6 +2475,11 @@ class CLIOptions
'--plan FILE', '--plan FILE',
'Follow given plan file, instead of using given git ref/sha' 'Follow given plan file, instead of using given git ref/sha'
) { |v| options[:plan] = v } ) { |v| options[:plan] = v }
opts.on(
'--clean-macho-binary FILE',
'Tool to clean duplicate RPATHs from given Mach-O binary.'
) { |v| options[:clean_macho_binary] = v }
end end
end end
end end
@@ -2144,6 +2496,17 @@ if __FILE__ == $PROGRAM_NAME
build.print_info build.print_info
elsif cli_options[:preview] elsif cli_options[:preview]
build.print_preview build.print_preview
elsif cli_options[:clean_macho_binary]
macho_cleaner = MachOCleaner.new(cli_options[:clean_macho_binary])
if macho_cleaner.duplicate_rpaths?
build.info 'Removing duplicate RPATHs from ' \
"#{cli_options[:clean_macho_binary]}..."
macho_cleaner.clean!
build.info 'Cleaned duplicate RPATHs successfully!'
else
build.info 'No duplicate RPATHs found.'
end
else else
build.build build.build
end end

8
flake.lock generated
View File

@@ -20,16 +20,16 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1732981179, "lastModified": 1767313136,
"narHash": "sha256-F7thesZPvAMSwjRu0K8uFshTk3ZZSNAsXTIFvXBT+34=", "narHash": "sha256-16KkgfdYqjaeRGBaYsNrhPRRENs0qzkQVUooNHtoy2w=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "62c435d93bf046a5396f3016472e8f7c8e2aed65", "rev": "ac62194c3917d5f474c1a844b6fd6da2db95077d",
"type": "github" "type": "github"
}, },
"original": { "original": {
"owner": "NixOS", "owner": "NixOS",
"ref": "nixos-24.11", "ref": "nixos-25.05",
"repo": "nixpkgs", "repo": "nixpkgs",
"type": "github" "type": "github"
} }

164
flake.nix
View File

@@ -2,88 +2,104 @@
description = "Development environment flake"; description = "Development environment flake";
inputs = { inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
}; };
outputs = { self, nixpkgs, flake-utils }: outputs =
flake-utils.lib.eachDefaultSystem (system: {
self,
nixpkgs,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (
system:
let let
pkgs = nixpkgs.legacyPackages.${system}; pkgs = nixpkgs.legacyPackages.${system};
# List of supported macOS SDK versions. # List of supported macOS SDK versions.
sdk_versions = [ "11" "12" "13" "14" "15" ]; sdk_versions = [
"11"
"12"
"13"
"14"
"15"
"26"
];
default_sdk_version = "11"; default_sdk_version = "11";
mkDevShell = { macos_version ? default_sdk_version }: mkDevShell =
{
macos_version ? default_sdk_version,
}:
let let
apple_sdk = pkgs.${"apple-sdk_${macos_version}"}; apple_sdk = pkgs.${"apple-sdk_${macos_version}"};
in in
pkgs.mkShell { pkgs.mkShell {
# Package list specifically excludes ncurses, so that we link # Package list specifically excludes ncurses, so that we link
# against the system version of ncurses. This ensures emacs' TUI # against the system version of ncurses. This ensures emacs' TUI
# works out of the box without the user having to manually set # works out of the box without the user having to manually set
# TERMINFO in the shell before launching emacs. # TERMINFO in the shell before launching emacs.
packages = with pkgs; [ packages = with pkgs; [
apple_sdk apple_sdk
autoconf autoconf
bash bash
cairo cairo
clang clang
coreutils coreutils
curl curl
darwin.DarwinTools # sw_vers darwin.DarwinTools # sw_vers
dbus dbus
expat expat
findutils findutils
gcc gcc
gettext gettext
giflib giflib
git git
gmp gmp
gnumake gnumake
gnupatch gnupatch
gnused gnused
gnutar gnutar
gnutls gnutls
harfbuzz harfbuzz
jansson jansson
jq jq
lcms2 lcms2
libffi libffi
libgccjit libgccjit
libiconv libiconv
libjpeg libjpeg
libpng libpng
librsvg librsvg
libtasn1 libtasn1
libunistring libunistring
libwebp libwebp
libxml2 libxml2
mailutils mailutils
nettle nettle
pkg-config pkg-config
python3 python313Packages.dmgbuild
rsync rsync
ruby_3_3 ruby
sqlite sqlite
texinfo texinfo
time time
tree-sitter tree-sitter
which which
xcbuild xcbuild
zlib zlib
]; ];
shellHook = '' shellHook = ''
export CC=clang export CC=clang
export MACOSX_DEPLOYMENT_TARGET="${macos_version}.0" export MACOSX_DEPLOYMENT_TARGET="${macos_version}.0"
export DEVELOPER_DIR="${apple_sdk}" export DEVELOPER_DIR="${apple_sdk}"
export NIX_LIBGCCJIT_VERSION="${pkgs.libgccjit.version}" export NIX_LIBGCCJIT_VERSION="${pkgs.libgccjit.version}"
export NIX_LIBGCCJIT_ROOT="${pkgs.libgccjit.outPath}" export NIX_LIBGCCJIT_ROOT="${pkgs.libgccjit.outPath}"
export BUNDLE_WITHOUT=development export BUNDLE_WITHOUT=development
''; '';
}; };
# Generate an attrset of shells for each macOS SDK version. # Generate an attrset of shells for each macOS SDK version.
versionShells = builtins.listToAttrs ( versionShells = builtins.listToAttrs (
@@ -93,10 +109,10 @@
}) sdk_versions }) sdk_versions
); );
in in
{ {
devShells = versionShells // { devShells = versionShells // {
default = mkDevShell {}; default = mkDevShell { };
}; };
} }
); );
} }

View File

@@ -1,67 +1,72 @@
DarwinTools-1 DarwinTools-1
autoconf-2.72 autoconf-2.72
bash-5.2p37 bash-5.2p37
bash-interactive-5.2p37
brotli-1.1.0 brotli-1.1.0
bzip2-1.0.8 bzip2-1.0.8
cairo-1.18.2 cairo-1.18.2
cctools-binutils-darwin-1010.6 cctools-binutils-darwin-1010.6
cctools-binutils-darwin-wrapper-1010.6 cctools-binutils-darwin-wrapper-1010.6
clang-16.0.6 clang-19.1.7
clang-wrapper-16.0.6 clang-wrapper-19.1.7
coreutils-9.5 coreutils-9.7
curl-8.11.0 curl-8.14.1
dbus-1.14.10 dbus-1.14.10
diffutils-3.10 diffutils-3.12
expat-2.6.4 expat-2.7.3
file-5.45 file-5.45
findutils-4.10.0 findutils-4.10.0
fontconfig-2.15.0 fontconfig-2.16.0
freetype-2.13.3 freetype-2.13.3
gawk-5.3.1 gawk-5.3.2
gcc-13.3.0 gcc-14.3.0
gcc-wrapper-13.3.0 gcc-wrapper-14.3.0
gdk-pixbuf-2.42.12 gdk-pixbuf-2.42.12
gettext-0.21.1 gettext-0.22.5
giflib-5.2.2 giflib-5.2.2
git-2.47.0 git-2.50.1
glib-2.82.1 glib-2.84.3
gnugrep-3.11 gnugrep-3.11
gnumake-4.4.1 gnumake-4.4.1
gnused-4.9 gnused-4.9
gnutar-1.35 gnutar-1.35
gnutls-3.8.6 gnutls-3.8.9
graphite2-1.3.14 graphite2-1.3.14
gzip-1.13 gzip-1.14
harfbuzz-10.0.1 harfbuzz-10.2.0
jq-1.7.1 jq-1.7.1
krb5-1.21.3 krb5-1.21.3
lcms2-2.16 lcms2-2.17
libdeflate-1.22 libdeflate-1.23
libgccjit-13.3.0 libgccjit-14.3.0
libiconv-107 libiconv-1.17
libidn2-2.3.7 libiconv-109
libidn2-2.3.8
libjpeg-turbo-3.0.4 libjpeg-turbo-3.0.4
libpng-apng-1.6.43 libpng-apng-1.6.46
libpsl-0.21.5 libpsl-0.21.5
librsvg-2.58.3 librsvg-2.60.0
libtasn1-4.19.0 libtasn1-4.20.0
libtiff-4.7.0 libtiff-4.7.0
libwebp-1.4.0 libwebp-1.5.0
libxml2-2.13.4 libxml2-2.13.8
mailutils-3.17 mailutils-3.18
nettle-3.10 nettle-3.10.1
nghttp2-1.64.0 nghttp2-1.65.0
openssl-3.3.2 openssl-3.4.3
patch-2.7.6 patch-2.7.6
pkg-config-wrapper-0.29.2 pkg-config-wrapper-0.29.2
python3-3.12.7 python3-3.13.5
rsync-3.3.0 python3.13-dmgbuild-1.6.2
ruby-3.3.5 python3.13-ds-store-1.3.1
sqlite-3.46.1 python3.13-mac-alias-2.2.2
texinfo-7.1.1 rsync-3.4.1
ruby-3.3.9
sqlite-3.48.0
texinfo-7.2
time-1.9 time-1.9
tree-sitter-0.24.3 tree-sitter-0.25.3
which-2.21 which-2.23
xcbuild-0.1.1-unstable-2019-11-20 xcbuild-0.1.1-unstable-2019-11-20
xz-5.6.3 xz-5.8.1
zstd-1.5.6 zstd-1.5.7

View File

@@ -287,7 +287,7 @@ func (s *Updater) createRepoFile(
s.logger.Info( s.logger.Info(
"new commit created", "new commit created",
"commit", contResp.GetSHA(), "message", contResp.GetMessage(), "commit", contResp.GetSHA(), "message", contResp.GetMessage(),
"url", contResp.Commit.GetHTMLURL(), "url", contResp.GetHTMLURL(),
) )
return nil return nil
@@ -354,7 +354,7 @@ func (s *Updater) updateRepoFile(
s.logger.Info( s.logger.Info(
"new commit created", "new commit created",
"commit", contResp.GetSHA(), "message", contResp.GetMessage(), "commit", contResp.GetSHA(), "message", contResp.GetMessage(),
"url", contResp.Commit.GetHTMLURL(), "url", contResp.GetHTMLURL(),
) )
return true, nil return true, nil

View File

@@ -38,6 +38,10 @@ func planCmd() *cli2.Command {
Name: "sha", Name: "sha",
Usage: "override commit SHA of specified git branch/tag", Usage: "override commit SHA of specified git branch/tag",
}, },
&cli2.IntFlag{
Name: "build-variant",
Usage: "build variant to add to the end of the version string",
},
&cli2.StringFlag{ &cli2.StringFlag{
Name: "format", Name: "format",
Aliases: []string{"f"}, Aliases: []string{"f"},
@@ -90,6 +94,7 @@ func planAction(c *cli2.Context, opts *Options) error {
EmacsRepo: c.String("emacs-repo"), EmacsRepo: c.String("emacs-repo"),
Ref: ref, Ref: ref,
SHAOverride: c.String("sha"), SHAOverride: c.String("sha"),
BuildVariant: c.Int("build-variant"),
OutputDir: c.String("output-dir"), OutputDir: c.String("output-dir"),
TestBuild: c.String("test-build"), TestBuild: c.String("test-build"),
TestBuildType: plan.Prerelease, TestBuildType: plan.Prerelease,

View File

@@ -65,10 +65,7 @@ func (s *OSInfo) distinctVersion(version string) string {
return parts[0] return parts[0]
} }
max := len(parts) end := min(len(parts), 2)
if max > 2 {
max = 2
}
return strings.Join(parts[0:max], ".") return strings.Join(parts[0:end], ".")
} }

View File

@@ -35,6 +35,7 @@ type Options struct {
EmacsRepo string EmacsRepo string
Ref string Ref string
SHAOverride string SHAOverride string
BuildVariant int
OutputDir string OutputDir string
TestBuild string TestBuild string
TestBuildType TestBuildType TestBuildType TestBuildType
@@ -95,6 +96,12 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { //nolint:funlen
releaseName = "Emacs." + version releaseName = "Emacs." + version
} }
if opts.BuildVariant != 0 {
variant := strconv.Itoa(opts.BuildVariant)
absoluteVersion += "-" + variant
releaseName += "-" + variant
}
// Attempt to get the macOS SDK version from the environment, if it's not // Attempt to get the macOS SDK version from the environment, if it's not
// available, use the version from the system. // available, use the version from the system.
targetMacOSVersion := osInfo.DistinctSDKVersion() targetMacOSVersion := osInfo.DistinctSDKVersion()
@@ -139,7 +146,7 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) { //nolint:funlen
plan.Build.Name += ".test." + testName plan.Build.Name += ".test." + testName
plan.Release.Title = "Test Builds (" + testName + ")" plan.Release.Title = "Test Builds (" + testName + ")"
plan.Release.Name = "test-builds" plan.Release.Name = "test-builds-" + testName
plan.Release.Prerelease = false plan.Release.Prerelease = false
plan.Release.Draft = true plan.Release.Draft = true

View File

@@ -4,7 +4,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"regexp" "regexp"
"strings"
) )
// Errors // Errors
@@ -18,8 +17,9 @@ var (
) )
var ( var (
stableVersion = regexp.MustCompile(`^\d+\.\d+(?:[a-z]+)?$`) stableVersion = regexp.MustCompile(`^\d+\.\d+(?:[a-z]+)?(-\d+)?$`)
stableGitRef = regexp.MustCompile(`^emacs-(\d+\.\d+(?:[a-z]+)?)$`) pretestVersion = regexp.MustCompile(`-pretest(-\d+)?$`)
stableGitRef = regexp.MustCompile(`^emacs-(\d+\.\d+(?:[a-z]+)?)$`)
) )
func VersionToName(version string) (string, error) { func VersionToName(version string) (string, error) {
@@ -28,7 +28,7 @@ func VersionToName(version string) (string, error) {
} }
if stableVersion.MatchString(version) || if stableVersion.MatchString(version) ||
strings.HasSuffix(version, "-pretest") { pretestVersion.MatchString(version) {
return "Emacs-" + version, nil return "Emacs-" + version, nil
} }

View File

@@ -30,6 +30,27 @@ func TestVersionToName(t *testing.T) {
}, },
want: "Emacs.2021-07-01.1b88404.master", want: "Emacs.2021-07-01.1b88404.master",
}, },
{
name: "nightly with variant",
args: args{
version: "2021-07-01.1b88404.master-1",
},
want: "Emacs.2021-07-01.1b88404.master-1",
},
{
name: "pretest",
args: args{
version: "30.0.93-pretest",
},
want: "Emacs-30.0.93-pretest",
},
{
name: "pretest with variant",
args: args{
version: "30.0.93-pretest-1",
},
want: "Emacs-30.0.93-pretest-1",
},
{ {
name: "stable", name: "stable",
args: args{ args: args{
@@ -44,6 +65,20 @@ func TestVersionToName(t *testing.T) {
}, },
want: "Emacs-23.3b", want: "Emacs-23.3b",
}, },
{
name: "stable with variant",
args: args{
version: "23.3-1",
},
want: "Emacs-23.3-1",
},
{
name: "stable with letter and variant",
args: args{
version: "23.3b-1",
},
want: "Emacs-23.3b-1",
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {