Compare commits

...

27 Commits

Author SHA1 Message Date
bc3923c9ca chore(release): 0.6.12 2021-08-03 00:19:21 +01:00
e6b1e5a554 fix(sign): resolve signing issue caused by re-linking shared lib in *.eln files
When shared libs are stored in `Contents/Frameworks`, the re-link path
for `/usr/local/lib/gcc/11/libgcc_s.1.dylib` within bundled *.eln files
becomes `@executable_path/../Frameworks/libgcc_s.1.dylib`, which seems to
not leave enough space in the *.eln binary header to add a code
signature with codesign.

This used to work when shared libraries were bundled into
`Contents/MacOS/lib`, leading to a shorter relink path of
`@executable_path/lib/libgcc_s.1.dylib`, which does leave enough space
to add a code signature to *.eln files.
2021-08-03 00:13:10 +01:00
94625fce38 chore(release): 0.6.11 2021-07-17 13:08:10 +01:00
b03343f506 fix(native-comp): fix re-linking and signing issue with *.eln files
With the recent move of shared libraries and native lisp *.eln files to
Contents/Frameworks, the re-linking paths became longer, causing code
signing to fail with headerpad errors. This change ensures there's
enough space within binary files for longer shared library relink paths,
and code signing payloads.
2021-07-17 13:04:33 +01:00
b1896d4a4f chore(release): 0.6.10 2021-07-17 00:20:42 +01:00
9d32509c61 fix(native-comp): *.eln files were not being found during shared lib embedding 2021-07-16 21:00:24 +01:00
1c2745cd36 docs(readme): mention binary builds repo 2021-07-10 21:19:11 +01:00
80a0d55b24 chore(release): 0.6.9 2021-07-04 23:28:28 +01:00
fd0ec4d772 fix(plan): correctly parse --test-release-type flag
The --test-release-type flag was essentially broken and ignored, always
creating a prerelease release for test builds. Now it can also produce
draft releases.
2021-07-04 23:27:00 +01:00
c0c809a86a style(lint): fix linting complaint 2021-07-03 02:39:01 +01:00
cb63806262 feat(release): add bulk edit command to quickly change multiple GitHub releases 2021-07-03 02:36:15 +01:00
6d7ab95ca2 chore(release): 0.6.8 2021-07-03 00:19:26 +01:00
f4d6e3a56d feat(builds): add support for stable builds
Stable builds are based off of release git tags in Emacs' git
repo. Examples of what release tags look like:

- emacs-26.1
- emacs-26.2
- emacs-26.3
- emacs-27.1
- emacs-27.2

When the specified git ref looks like a stable release, the plan command
will generate a release a different and simpler release name that does
not include the date, git sha or ref. Instead, for "emacs-27.2" for
example, the emacs-builds release name will be "Emacs-27.2".

The "build name", used for naming the disk image, still retains the same
format as the nightly builds.

Also, non-stable releases are now marked as pre-release on GitHub by
default.

The reason for the different release name format for stable builds is
both to separate them, but also to make it easier to keep the version of
the homebrew cask as simply "27.2".
2021-07-03 00:11:59 +01:00
3f1059940d chore(release): 0.6.7 2021-07-02 16:50:25 +01:00
5c722e36c5 feat(bundle): move bundled shared libraries to Contents/Frameworks
The Frameworks folder is the recommended location to store shared
libraries within macOS application bundles. Previously we stored them in
Contents/MacOS/lib.

Latest nightly builds already store all *.eln files under the Frameworks
folder, so it seemed like a good time to make the change with the
library bundler/embedder too.
2021-07-02 16:49:32 +01:00
f52dd8dc6d chore(release): 0.6.6 2021-07-01 23:52:50 +01:00
4cdbaf1ec0 chore(cask): fix cask vs formula terminology
Homebrew casks are their own thing, separate from formulas. Hence remove
all mentions to "formula" to avoid confusion.
2021-07-01 23:50:50 +01:00
f3a289b11c chore(release): 0.6.5 2021-07-01 23:37:21 +01:00
9019e73d60 fix(native_comp): improve handling of *.eln files in .app bundle
Specifically support latest changes in master which places *.eln files
within the .app bundle in "Contents/Frameworks".
2021-07-01 23:33:50 +01:00
28930381a8 chore(release): 0.6.4 2021-06-30 10:41:34 +01:00
df25e54ef7 chore(sign): simplify *.eln locating logic
Instead of only checking very specific paths within the .app bundle,
just check the whole bundle for any and all *.eln files.
2021-06-30 10:20:22 +01:00
6d21d1bef4 chore(release): 0.6.3 2021-06-29 14:40:18 +01:00
99aa76b398 fix(patches): correctly set ref when loading a build plan YAML
The ref was not correctly set when loading a build plan, resulting in
the set of patches being selected were always for Emacs 28.x, preventing
builds of Emacs 27.x and 26.x.
2021-06-29 14:37:43 +01:00
b60ca528f8 chore(release): 0.6.2 2021-06-29 01:30:19 +01:00
23b8236e0a fix(native_comp): patch Emacs.pdmp for customized native-lisp paths
In my initial testing without full native-comp AoT, Emacs seemed to
somehow launch fine. But with a AoT build it complains that it can't
find *.eln files in the original paths that contained dots. But since we
have to customize those folder names removing the dots to make Apple's
codesign happy, we also need to update Emacs.pdmp too.
2021-06-29 01:27:16 +01:00
56d0364099 chore(release): 0.6.1 2021-06-28 22:50:20 +01:00
6af597b427 fix(cask): add missing --force flag to cask update command 2021-06-28 22:49:07 +01:00
13 changed files with 544 additions and 94 deletions

View File

@@ -2,6 +2,85 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.6.12](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.11...v0.6.12) (2021-08-02)
### Bug Fixes
* **sign:** resolve signing issue caused by re-linking shared lib in *.eln files ([e6b1e5a](https://github.com/jimeh/build-emacs-for-macos/commit/e6b1e5a554fd0f776bd01c17cfb1ebbbdf7a7831))
### [0.6.11](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.10...v0.6.11) (2021-07-17)
### Bug Fixes
* **native-comp:** fix re-linking and signing issue with *.eln files ([b03343f](https://github.com/jimeh/build-emacs-for-macos/commit/b03343f506aa3ceabdfa03f8a2916b2db4873f3f))
### [0.6.10](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.9...v0.6.10) (2021-07-16)
### Bug Fixes
* **native-comp:** *.eln files were not being found during shared lib embedding ([9d32509](https://github.com/jimeh/build-emacs-for-macos/commit/9d32509c615076618957cc47c82f6e9d8f972fe7))
### [0.6.9](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.8...v0.6.9) (2021-07-04)
### Features
* **release:** add bulk edit command to quickly change multiple GitHub releases ([cb63806](https://github.com/jimeh/build-emacs-for-macos/commit/cb638062625d9bc3eee12515067fb09e05a08414))
### Bug Fixes
* **plan:** correctly parse --test-release-type flag ([fd0ec4d](https://github.com/jimeh/build-emacs-for-macos/commit/fd0ec4d772dd3da93afc234fb3024220b2099c88))
### [0.6.8](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.7...v0.6.8) (2021-07-02)
### Features
* **builds:** add support for stable builds ([f4d6e3a](https://github.com/jimeh/build-emacs-for-macos/commit/f4d6e3a56d2c15b0c86af18e8d16bebbeb92a8ab))
### [0.6.7](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.6...v0.6.7) (2021-07-02)
### Features
* **bundle:** move bundled shared libraries to Contents/Frameworks ([5c722e3](https://github.com/jimeh/build-emacs-for-macos/commit/5c722e36c571aa7bf558b7f210c011f12d8d8a1c))
### [0.6.6](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.5...v0.6.6) (2021-07-01)
### [0.6.5](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.4...v0.6.5) (2021-07-01)
### Bug Fixes
* **native_comp:** improve handling of *.eln files in .app bundle ([9019e73](https://github.com/jimeh/build-emacs-for-macos/commit/9019e73d606f0379f988f46d6008770f8f3f7a51))
### [0.6.4](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.3...v0.6.4) (2021-06-30)
### [0.6.3](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.2...v0.6.3) (2021-06-29)
### Bug Fixes
* **patches:** correctly set ref when loading a build plan YAML ([99aa76b](https://github.com/jimeh/build-emacs-for-macos/commit/99aa76b3985195c310a20bafa19a8c7a4c8558fd))
### [0.6.2](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.1...v0.6.2) (2021-06-29)
### Bug Fixes
* **native_comp:** patch Emacs.pdmp for customized native-lisp paths ([23b8236](https://github.com/jimeh/build-emacs-for-macos/commit/23b8236e0a66fb09810e8422bedf02f7192a53e4))
### [0.6.1](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.0...v0.6.1) (2021-06-28)
### Bug Fixes
* **cask:** add missing --force flag to cask update command ([6af597b](https://github.com/jimeh/build-emacs-for-macos/commit/6af597b4271341f9796c3d9c356de9918e0f6f85))
## [0.6.0](https://github.com/jimeh/build-emacs-for-macos/compare/v0.5.2...v0.6.0) (2021-06-28)

View File

@@ -45,6 +45,11 @@ The build produced does have some limitations:
that built the application will yield warnings. If you want to make a signed
Emacs.app, google is you friend for finding signing instructions.
## Binary Builds
Nightly and stable binary builds produced with this build script are available
from [jimeh/emacs-builds](https://github.com/jimeh/emacs-builds).
## Requirements
- [Xcode](https://apps.apple.com/gb/app/xcode/id497799835?mt=12)

View File

@@ -11,6 +11,7 @@ require 'net/http'
require 'optparse'
require 'pathname'
require 'time'
require 'tmpdir'
require 'uri'
require 'yaml'
@@ -117,9 +118,10 @@ class Build
def load_plan(filename)
plan = YAML.safe_load(File.read(filename), [:Time])
@ref = plan.dig('source', 'ref')
@meta = {
sha: plan.dig('source', 'commit', 'sha'),
ref: plan.dig('source', 'ref'),
ref: @ref,
date: plan.dig('source', 'commit', 'date')
}
@@ -292,6 +294,8 @@ class Build
"-L#{gcc_info.libgccjit_lib_dir}",
"-I#{File.join(gcc_info.root_dir, 'include')}",
"-I#{File.join(gcc_info.libgccjit_root_dir, 'include')}",
# Ensure library re-linking and code signing will work after building.
'-Wl,-headerpad_max_install_names',
ENV['LDFLAGS']
].compact.join(' ')
@@ -411,20 +415,17 @@ class Build
contents_dir = File.join(app, 'Contents')
FileUtils.cd(contents_dir) do
# Skip creation of symlinks if *.eln files are located under
# Resources/native-lisp. Emacs is capable of finding lisp sources and
# *.eln cache files without symlinks.
return if Dir['Resources/native-lisp/**/*.eln'].any?
info 'Creating symlinks within Emacs.app needed for native-comp'
FileUtils.ln_s('Resources/lisp', 'lisp') unless File.exist?('lisp')
source = Dir['MacOS/libexec/emacs/**/eln-cache',
'MacOS/lib/emacs/**/native-lisp'].first
if source.nil?
err 'Failed to find native-lisp cache directory for symlink creation.'
# Skip creation of symlinks if *.eln files are not located in a location
# known to be used by builds which need symlinks and other tweaks.
return if source.nil?
info 'Creating symlinks within Emacs.app needed for native-comp'
if !File.exist?('lisp') && File.exist?('Resources/lisp')
FileUtils.ln_s('Resources/lisp', 'lisp')
end
# Check for folder name containing two dots (.), as this causes Apple's
@@ -434,10 +435,9 @@ class Build
# The workaround for now is to rename the folder replacing the dots with
# hyphens (-), and create the native-lisp symlink pointing to the new
# location.
if source.match(%r{/.+\..+\..+/})
# Dig deeper into native-lisp directory
eln_dir = File.dirname(Dir[File.join(source, '**', '*.eln')].first)
eln_dir = File.dirname(Dir[File.join(source, '**', '*.eln')].first)
if eln_dir.match(%r{/.+\..+\..+/})
base = File.basename(eln_dir)
parent = File.dirname(eln_dir)
@@ -454,6 +454,13 @@ class Build
parent = File.dirname(parent)
end
eln_parts = eln_dir.match(
%r{/(\d+\.\d+\.\d+)/native-lisp/(\d+\.\d+\.\d+-\w+)(?:/.+)?$}i
)
if eln_parts
patch_dump_native_lisp_paths(app, eln_parts[1], eln_parts[2])
end
# Find native-lisp directory again after it has been renamed.
source = Dir['MacOS/libexec/emacs/**/eln-cache',
'MacOS/lib/emacs/**/native-lisp'].first
@@ -468,6 +475,29 @@ class Build
end
end
def patch_dump_native_lisp_paths(app, emacs_version, eln_version)
sanitized_emacs_version = emacs_version.gsub('.', '-')
sanitized_eln_version = eln_version.gsub('.', '-')
contents_dir = File.join(app, 'Contents')
FileUtils.cd(contents_dir) do
filename = Dir['MacOS/Emacs.pdmp', 'MacOS/libexec/Emacs.pdmp'].first
err "no Emacs.pdmp file found in #{app}" unless filename
info 'patching Emacs.pdmp to point at new native-lisp paths'
content = File.read(filename, mode: 'rb').gsub(
"lib/emacs/#{emacs_version}/native-lisp/#{eln_version}/",
"lib/emacs/#{sanitized_emacs_version}/" \
"native-lisp/#{sanitized_eln_version}/"
).gsub(
"../native-lisp/#{eln_version}/",
"../native-lisp/#{sanitized_eln_version}/"
)
File.open(filename, 'w') { |f| f.write(content) }
end
end
def add_cli_helper(app)
source = File.join(__dir__, 'helper', 'emacs-cli.bash')
target = File.join(app, 'Contents', 'MacOS', 'bin', 'emacs')
@@ -708,8 +738,6 @@ class AbstractEmbedder
@app = app
end
private
def invocation_dir
File.join(app, 'Contents', 'MacOS')
end
@@ -757,19 +785,7 @@ class LibEmbedder < AbstractEmbedder
private
def eln_files
@eln_files ||= Dir[
File.join(
app, 'Contents', 'Resources', 'native-lisp', '**', '*.eln'
),
File.join(
app, 'Contents', 'MacOS', 'lib', 'emacs', '**',
'native-lisp', '**', '*.eln'
),
File.join(
app, 'Contents', 'MacOS', 'libexec', 'emacs', '**',
'eln-cache', '**', '*.eln'
)
]
@eln_files ||= Dir[File.join(app, 'Contents', '**', '*.eln')]
end
def copy_libs(exe, rel_path = nil)
@@ -875,15 +891,15 @@ class GccLibEmbedder < AbstractEmbedder
end
def target_dir
File.join(invocation_dir, gcc_info.relative_lib_dir)
File.join(lib_dir, gcc_info.relative_lib_dir)
end
def source_darwin_dir
File.join(invocation_dir, gcc_info.relative_darwin_lib_dir)
File.join(lib_dir, gcc_info.relative_darwin_lib_dir)
end
def target_darwin_dir
File.join(invocation_dir, gcc_info.sanitized_relative_darwin_lib_dir)
File.join(lib_dir, gcc_info.sanitized_relative_darwin_lib_dir)
end
def source_dir
@@ -939,7 +955,21 @@ class GccInfo
def sanitized_relative_darwin_lib_dir
@sanitized_relative_darwin_lib_dir ||= File.join(
File.dirname(relative_darwin_lib_dir),
File.basename(relative_darwin_lib_dir).split('.').first
File.basename(relative_darwin_lib_dir).gsub('.', '_')
)
end
def app_bundle_relative_lib_dir
@app_bundle_relative_lib_dir ||= relative_dir(
File.join(embedder.lib_dir, relative_lib_dir),
embedder.invocation_dir
)
end
def app_bundle_relative_darwin_lib_dir
@app_bundle_relative_darwin_lib_dir ||= relative_dir(
File.join(embedder.lib_dir, sanitized_relative_darwin_lib_dir),
embedder.invocation_dir
)
end
@@ -986,6 +1016,10 @@ class GccInfo
private
def embedder
@embedder ||= AbstractEmbedder.new(Dir.mktmpdir(['Emacs', '.app']))
end
def relative_dir(path, root)
Pathname.new(path).relative_path_from(Pathname.new(root)).to_s
end

View File

@@ -1,8 +1,8 @@
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index 8c638312b0..87af889ef4 100644
index 638d4b274c..2599211936 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -4215,6 +4215,52 @@ native-compile-async
@@ -4224,6 +4224,52 @@ native-compile-async
(let ((load (not (not load))))
(native--compile-async files recursively load selector)))
@@ -16,10 +16,10 @@ index 8c638312b0..87af889ef4 100644
+ (devtools-dir
+ "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib")
+ (gcc-dir (expand-file-name
+ "<%= relative_lib_dir %>"
+ "<%= app_bundle_relative_lib_dir %>"
+ invocation-directory))
+ (darwin-dir (expand-file-name
+ "<%= sanitized_relative_darwin_lib_dir %>"
+ "<%= app_bundle_relative_darwin_lib_dir %>"
+ invocation-directory))
+ (lib-paths (list)))
+

View File

@@ -19,6 +19,7 @@ import (
"github.com/hexops/gotextdiff/myers"
"github.com/hexops/gotextdiff/span"
"github.com/jimeh/build-emacs-for-macos/pkg/gh"
"github.com/jimeh/build-emacs-for-macos/pkg/release"
"github.com/jimeh/build-emacs-for-macos/pkg/repository"
)
@@ -42,11 +43,11 @@ type UpdateOptions struct {
// BuildsRepo is the GitHub repository containing binary releases.
BuildsRepo *repository.Repository
// TapRepo is the GitHub repository to update the cask formula in.
// TapRepo is the GitHub repository to update the casks in.
TapRepo *repository.Repository
// Ref is the git ref to apply cask formula updates on top of. Default
// branch will be used if empty.
// Ref is the git ref to apply cask updates on top of. Default branch will
// be used if empty.
Ref string
// OutputDir specifies a directory to write cask files to. When set, tap
@@ -56,10 +57,10 @@ type UpdateOptions struct {
// Force update will ignore the outdated live check flag, and process all
// casks regardless. But it will only update the cask in question if the
// resulting output cask formula is different.
// resulting output cask is different.
Force bool
// TemplatesDir is the directory where cask formula templates are located.
// TemplatesDir is the directory where cask templates are located.
TemplatesDir string
LiveChecks []*LiveCheck
@@ -150,7 +151,7 @@ func (s *Updater) putFile(
content []byte,
) (bool, error) {
parent := filepath.Dir(filename)
s.logger.Info("processing formula update",
s.logger.Info("processing cask update",
"output-directory", parent, "cask", chk.Cask, "file", filename,
)
@@ -164,18 +165,18 @@ func (s *Updater) putFile(
return false, err
}
infoMsg := "creating formula"
infoMsg := "creating cask"
if !os.IsNotExist(err) {
infoMsg = "updating formula"
infoMsg = "updating cask"
if bytes.Equal(existingContent, content) {
s.logger.Info(
"skip update: no change to cask formula content",
"skip update: no change to cask content",
"cask", chk.Cask, "file", filename,
)
s.logger.Debug(
"formula content",
"cask content",
"file", filename, "content", string(content),
)
@@ -198,7 +199,7 @@ func (s *Updater) putFile(
)
s.logger.Debug(
"formula content",
"cask content",
"file", filename, "content", string(content),
)
@@ -218,7 +219,7 @@ func (s *Updater) putRepoFile(
filename string,
content []byte,
) (bool, error) {
s.logger.Info("processing formula update",
s.logger.Info("processing cask update",
"tap-repo", repo.Source, "cask", chk.Cask, "file", filename,
)
repoContent, _, resp, err := s.gh.Repositories.GetContents(
@@ -264,12 +265,12 @@ func (s *Updater) createRepoFile(
diff := fmt.Sprint(gotextdiff.ToUnified(filename, filename, "", edits))
s.logger.Info(
"creating formula",
"creating cask",
"cask", chk.Cask, "version", chk.Version.Latest, "file", filename,
"diff", diff,
)
s.logger.Debug(
"formula content",
"cask content",
"file", filename, "content", string(content),
)
contResp, _, err := s.gh.Repositories.CreateFile(
@@ -307,7 +308,7 @@ func (s *Updater) updateRepoFile(
if existingContent == string(content) {
s.logger.Info(
"skip update: no change to formula content",
"skip update: no change to cask content",
"cask", chk.Cask, "file", filename,
)
@@ -329,12 +330,12 @@ func (s *Updater) updateRepoFile(
))
s.logger.Info(
"updating formula",
"updating cask",
"cask", chk.Cask, "version", chk.Version.Latest, "file", filename,
"diff", diff,
)
s.logger.Debug(
"formula content",
"cask content",
"file", filename, "content", string(content),
)
@@ -363,9 +364,14 @@ func (s *Updater) renderCask(
ctx context.Context,
chk *LiveCheck,
) ([]byte, error) {
releaseName := "Emacs." + chk.Version.Latest
releaseName, err := release.VersionToName(chk.Version.Latest)
if err != nil {
return nil, err
}
s.logger.Info("fetching release details", "release", releaseName)
s.logger.Info("fetching release details",
"release", releaseName, "repo", s.BuildsRepo.URL(),
)
release, resp, err := s.gh.Repositories.GetReleaseByTag(
ctx, s.BuildsRepo.Owner(), s.BuildsRepo.Name(), releaseName,
)

View File

@@ -23,7 +23,7 @@ func caskCmd() *cli2.Command {
return &cli2.Command{
Name: "cask",
Usage: "manage Homebrew Cask formula",
Usage: "manage Homebrew Casks",
Flags: []cli2.Flag{
&cli2.StringFlag{
Name: "builds-repository",
@@ -97,6 +97,14 @@ func caskUpdateCmd() *cli2.Command {
EnvVars: []string{"CASK_TEMPLATE_DIR"},
Required: true,
},
&cli2.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Usage: "force update file even if livecheck has it marked " +
"as not outdated (does not force update if cask " +
"content is unchanged)",
Value: false,
},
},
Action: caskActionWrapper(caskUpdateAction),
}
@@ -112,6 +120,7 @@ func caskUpdateAction(
GithubToken: cOpts.GithubToken,
Ref: c.String("ref"),
OutputDir: c.String("output"),
Force: c.Bool("force"),
TemplatesDir: c.String("templates-dir"),
}

View File

@@ -89,7 +89,7 @@ func planAction(c *cli2.Context, opts *Options) error {
GithubToken: c.String("github-token"),
}
if c.String("test-build-type") == "draft" {
if c.String("test-release-type") == "draft" {
planOpts.TestBuildType = plan.Draft
}

View File

@@ -1,6 +1,7 @@
package cli
import (
"errors"
"fmt"
"os"
"path/filepath"
@@ -60,6 +61,7 @@ func releaseCmd() *cli2.Command {
Subcommands: []*cli2.Command{
releaseCheckCmd(),
releasePublishCmd(),
releaseBulkCmd(),
},
}
}
@@ -206,3 +208,56 @@ func releasePublishAction(
return release.Publish(c.Context, rlsOpts)
}
func releaseBulkCmd() *cli2.Command {
return &cli2.Command{
Name: "bulk",
Usage: "bulk modify GitHub releases",
ArgsUsage: "",
Flags: []cli2.Flag{
&cli2.StringFlag{
Name: "name",
Usage: "regexp pattern matching release names to modify",
},
&cli2.StringFlag{
Name: "prerelease",
Usage: "change prerelease flag, must be \"true\" or " +
"\"false\", otherwise prerelease value is not changed",
},
&cli2.BoolFlag{
Name: "dry-run",
Usage: "do not perform any changes",
},
},
Action: releaseActionWrapper(releaseBulkAction),
}
}
func releaseBulkAction(
c *cli2.Context,
opts *Options,
rOpts *releaseOptions,
) error {
bulkOpts := &release.BulkOptions{
Repository: rOpts.Repository,
NamePattern: c.String("name"),
DryRun: c.Bool("dry-run"),
GithubToken: rOpts.GithubToken,
}
switch c.String("prerelease") {
case "true":
v := true
bulkOpts.Prerelease = &v
case "false":
v := false
bulkOpts.Prerelease = &v
case "":
default:
return errors.New(
"--prerelease by me \"true\" or \"false\" when specified",
)
}
return release.Bulk(c.Context, bulkOpts)
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/jimeh/build-emacs-for-macos/pkg/commit"
"github.com/jimeh/build-emacs-for-macos/pkg/gh"
"github.com/jimeh/build-emacs-for-macos/pkg/osinfo"
"github.com/jimeh/build-emacs-for-macos/pkg/release"
"github.com/jimeh/build-emacs-for-macos/pkg/repository"
)
@@ -64,15 +65,17 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) {
return nil, err
}
releaseName := fmt.Sprintf(
"Emacs.%s.%s.%s",
version := fmt.Sprintf(
"%s.%s.%s",
commitInfo.DateString(),
commitInfo.ShortSHA(),
sanitizeString(opts.Ref),
)
releaseName := fmt.Sprintf("Emacs.%s", version)
buildName := fmt.Sprintf(
"%s.%s.%s",
releaseName,
"Emacs.%s.%s.%s",
version,
sanitizeString(osInfo.Name+"-"+osInfo.MajorMinor()),
sanitizeString(osInfo.Arch),
)
@@ -92,7 +95,8 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) {
},
OS: osInfo,
Release: &Release{
Name: releaseName,
Name: releaseName,
Prerelease: true,
},
Output: &Output{
Directory: opts.OutputDir,
@@ -100,16 +104,28 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) {
},
}
// If given git ref is a stable release tag (emacs-23.2b, emacs-27.2, etc.)
// we modify release properties accordingly.
if v, err := release.GitRefToStableVersion(opts.Ref); err == nil {
plan.Release.Prerelease = false
plan.Release.Name, err = release.VersionToName(v)
if err != nil {
return nil, err
}
}
if opts.TestBuild != "" {
testName := sanitizeString(opts.TestBuild)
plan.Build.Name += ".test." + testName
plan.Release.Title = "Test Builds"
plan.Release.Name = "test-builds"
plan.Release.Prerelease = true
plan.Release.Draft = false
if opts.TestBuildType == Draft {
plan.Release.Prerelease = false
plan.Release.Draft = true
} else {
plan.Release.Prerelease = true
}
index := strings.LastIndex(diskImage, ".")

84
pkg/release/bulk.go Normal file
View File

@@ -0,0 +1,84 @@
package release
import (
"context"
"regexp"
"github.com/google/go-github/v35/github"
"github.com/hashicorp/go-hclog"
"github.com/jimeh/build-emacs-for-macos/pkg/gh"
"github.com/jimeh/build-emacs-for-macos/pkg/repository"
)
type BulkOptions struct {
Repository *repository.Repository
NamePattern string
Prerelease *bool
DryRun bool
GithubToken string
}
func Bulk(ctx context.Context, opts *BulkOptions) error {
logger := hclog.FromContext(ctx).Named("release")
gh := gh.New(ctx, opts.GithubToken)
nameMatcher, err := regexp.Compile(opts.NamePattern)
if err != nil {
return err
}
nextPage := 1
lastPage := 1
for nextPage <= lastPage {
releases, resp, err := gh.Repositories.ListReleases(
ctx, opts.Repository.Owner(), opts.Repository.Name(),
&github.ListOptions{
Page: nextPage,
PerPage: 100,
},
)
if err != nil {
return err
}
nextPage = resp.NextPage
lastPage = resp.LastPage
for _, r := range releases {
if !nameMatcher.MatchString(r.GetName()) {
continue
}
logger.Info("match found", "release", r.GetName())
var changes []interface{}
if opts.Prerelease != nil && r.GetPrerelease() != *opts.Prerelease {
changes = append(changes, "prerelease", *opts.Prerelease)
r.Prerelease = opts.Prerelease
}
if len(changes) > 0 {
changes = append(
[]interface{}{"release", r.GetName()}, changes...,
)
logger.Info("modifying", changes...)
if !opts.DryRun {
_, _, err = gh.Repositories.EditRelease(
ctx, opts.Repository.Owner(), opts.Repository.Name(),
r.GetID(), r,
)
if err != nil {
return err
}
}
}
}
if nextPage == 0 || lastPage == 0 {
break
}
}
return nil
}

42
pkg/release/version.go Normal file
View File

@@ -0,0 +1,42 @@
package release
import (
"errors"
"fmt"
"regexp"
)
// Errors
var (
Err = errors.New("release")
ErrInvalidName = fmt.Errorf("%w: invalid name", Err)
ErrEmptyVersion = fmt.Errorf("%w: empty version", Err)
ErrNotStableRef = fmt.Errorf(
"%w: git ref is not stable tagged release", Err,
)
)
var (
stableVersion = regexp.MustCompile(`^\d+\.\d+(?:[a-z]+)?$`)
stableGetRef = regexp.MustCompile(`^emacs-(\d+\.\d+(?:[a-z]+)?)$`)
)
func VersionToName(version string) (string, error) {
if version == "" {
return "", ErrEmptyVersion
}
if stableVersion.MatchString(version) {
return "Emacs-" + version, nil
}
return "Emacs." + version, nil
}
func GitRefToStableVersion(ref string) (string, error) {
if m := stableGetRef.FindStringSubmatch(ref); len(m) > 1 {
return m[1], nil
}
return "", fmt.Errorf("%w: \"%s\"", ErrNotStableRef, ref)
}

138
pkg/release/version_test.go Normal file
View File

@@ -0,0 +1,138 @@
package release
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestVersionToName(t *testing.T) {
type args struct {
version string
}
tests := []struct {
name string
args args
want string
wantErr string
}{
{
name: "empty",
args: args{
version: "",
},
wantErr: "release: empty version",
},
{
name: "nightly",
args: args{
version: "2021-07-01.1b88404.master",
},
want: "Emacs.2021-07-01.1b88404.master",
},
{
name: "stable",
args: args{
version: "27.2",
},
want: "Emacs-27.2",
},
{
name: "stable with letter",
args: args{
version: "23.3b",
},
want: "Emacs-23.3b",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := VersionToName(tt.args.version)
assert.Equal(t, tt.want, got)
if tt.wantErr != "" {
assert.EqualError(t, err, tt.wantErr)
} else {
assert.NoError(t, err)
}
})
}
}
func TestGitRefToStableVersion(t *testing.T) {
type args struct {
version string
}
tests := []struct {
name string
args args
want string
wantErr string
}{
{
name: "empty",
args: args{
version: "",
},
wantErr: "release: git ref is not stable tagged release: \"\"",
},
{
name: "master",
args: args{
version: "master",
},
wantErr: "release: git ref is not stable tagged release: " +
"\"master\"",
},
{
name: "feature",
args: args{
version: "feature/native-comp",
},
wantErr: "release: git ref is not stable tagged release: " +
"\"feature/native-comp\"",
},
{
name: "stable",
args: args{
version: "emacs-27.2",
},
want: "27.2",
},
{
name: "stable with letter",
args: args{
version: "emacs-23.3b",
},
want: "23.3b",
},
{
name: "future stable",
args: args{
version: "emacs-239.33",
},
want: "239.33",
},
{
name: "future stable with letter",
args: args{
version: "emacs-239.33c",
},
want: "239.33c",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := GitRefToStableVersion(tt.args.version)
assert.Equal(t, tt.want, got)
if tt.wantErr != "" {
assert.EqualError(t, err, tt.wantErr)
} else {
assert.NoError(t, err)
}
})
}
}

View File

@@ -117,42 +117,24 @@ func signCLIHelper(ctx context.Context, appBundle string, opts *Options) error {
}
// elnFiles finds all native-compilation *.eln files within a Emacs.app bundle,
// based on expected paths they might be stored in.
// excluding any *.eln which should be automatically located by codesign when
// signing the Emacs.app bundle itself with the --deep flag. Essentially this
// only returns *.eln files which must be individually signed before signing the
// app bundle itself.
func elnFiles(emacsApp string) ([]string, error) {
dirs := []string{
// Current *.eln location.
filepath.Join(emacsApp, "Contents", "Resources", "native-lisp"),
// Legacy *.eln location.
filepath.Join(emacsApp, "Contents", "MacOS", "lib", "emacs"),
}
var files []string
walkDirFunc := func(path string, _d fs.DirEntry, _err error) error {
if strings.HasSuffix(path, ".eln") {
walkDirFunc := func(path string, d fs.DirEntry, _err error) error {
if d.Type().IsRegular() && strings.HasSuffix(path, ".eln") &&
!strings.Contains(path, ".app/Contents/Frameworks/") {
files = append(files, path)
}
return nil
}
for _, dir := range dirs {
fi, err := os.Stat(dir)
if err != nil {
if os.IsNotExist(err) {
continue
}
return nil, err
}
if !fi.IsDir() {
continue
}
err = filepath.WalkDir(dir, walkDirFunc)
if err != nil {
return nil, err
}
err := filepath.WalkDir(filepath.Join(emacsApp, "Contents"), walkDirFunc)
if err != nil {
return nil, err
}
return files, nil