Compare commits

..

67 Commits

Author SHA1 Message Date
9c29f721b4 chore(release): 0.6.23 2021-11-10 22:19:28 +00:00
4ac71ddf39 docs(readme): update usage section with latest help text 2021-11-10 22:19:06 +00:00
d338c136db fix(build): do not re-link eln files by default
Relinking is currently failing for some of the *.eln files. See #60 for
details of the error.

Long-term plan is to investigate native-comp's compilation options to
hopefully resolve this issue.
2021-11-10 22:11:13 +00:00
d054a17fc7 chore(release): 0.6.22 2021-11-10 01:56:19 +00:00
4030ceb9ca feat(patch): add support for posix-spawn patch from emacs-plus 2021-11-10 01:55:38 +00:00
ad1a7dd62c chore(release): 0.6.21 2021-10-27 21:45:43 +01:00
665e6addec Merge pull request #59 from jimeh/improve-native-comp-patch 2021-10-27 21:42:05 +01:00
7f2aba0e4e chore(builder): add hidden sign-files command for testing purposes
Useful when needing to sign singular files with same ease of signing a
Emacs.app bundle.
2021-10-27 21:38:29 +01:00
be326b22aa refactor(native-comp): improve native-comp env setup patch
Instead of patching emacs sources before building Emacs, we can use
site-start.el instead for a much cleaner way of setting LIBRARY_PATH
before the user config is loaded.
2021-10-27 21:38:29 +01:00
ef4f2ad930 refactor: use site-start.el instead of abusing subdirs.el
Set source-directory in site-start.el instead of subdirs.el which we
were basically abusing to execute Elisp before the user config is
loaded.
2021-10-27 21:38:28 +01:00
2293c87fc9 refactor(build): turn add_cli_helper function into a embedder class 2021-10-27 03:12:30 +01:00
97178bf77a chore(release): 0.6.20 2021-10-23 20:35:09 +01:00
591c39e629 fix(notarization): explicitly only copy *.c and *.h C source files
Previously we just copies the whole "src" directory from the workdir, it
turns out it has some binary files after a successful build, which
caused Apple's notarization process to fail.

As we actually only care about the *.c and *.h files from the "src"
directory, let's explicitly only copy those files.
2021-10-23 20:32:13 +01:00
d6c99f8c60 chore(release): 0.6.19 2021-10-23 17:26:50 +01:00
bcbd01778d fix(patch): resolve emacs-29 symlink patches to their real URL
Annoyingly, when downloading raw files from GitHub, symlinks do not
return the content of the symlink target, but instead it simply returns
the symlink target path (../emacs-28/fix-window-role.patch).

Hence we have to check the patch file content, and if it contains only a
single line of text, we assume it's a symlink, and resolve it relative
to the original download URL.
2021-10-23 17:24:45 +01:00
5602475542 chore(release): 0.6.18 2021-10-23 16:20:44 +01:00
67b8c5f397 feat(docs): embed C source files and set source-directory accordingly
This enables the original C source to be linked to when describing
entities which are written in C.
2021-10-23 16:18:05 +01:00
4dad5812fa fix(patches): correctly use emacs 28.x and 29.x patches
The master branch is now Emacs 29.x, and Emacs 28.x lives on the
"emacs-28" release branch.
2021-10-23 16:15:39 +01:00
65bdff0b6d chore(release): 0.6.17 2021-10-10 22:42:01 +01:00
a956dc1b42 feat(release): tweak GitHub release description 2021-10-10 22:41:26 +01:00
af23b63518 chore(release): 0.6.16 2021-10-10 18:44:41 +01:00
9c20f77fe3 Merge pull request #58 from jimeh/release-improvements 2021-10-10 18:42:37 +01:00
2e2f9bc98a feat(build): handle macOS Big Sur and later version number 2021-10-10 18:25:38 +01:00
d63cd545aa feat(cask): make cask template helpers more flexible
Helpers available in cask templates can now accept one or more strings
as input, and first asset filename that matches all given strings is
returned.
2021-10-10 18:25:38 +01:00
1bbfe5d3ea feat(plan): allow build plan to be output as YAML or JSON 2021-10-10 18:25:37 +01:00
b4c5184cef fix(release): publish arguments are not handled as asset files to upload 2021-10-10 18:25:37 +01:00
e7a991ef92 feat(release): force-replace existing asset files by default
The file-size check is obviously not a very reliable way to determine if
the local and remote files are different. Hence we now default to always
uploading all given asset files, replacing any existing ones with the
same file name.

The size check logic is still available via the --asset-size-check flag.
2021-10-10 18:25:37 +01:00
7118ed8560 feat(release): add description to GitHub Releases
The description includes links to the Emacs source repo used, the git
ref, commit, tarball download URL, and build log (GitHub Actions Run).
2021-10-10 18:25:36 +01:00
a71cbda511 chore(release): remove default release repository value
This should be explicitly provided via environment variable of command
line flag.
2021-10-10 18:25:36 +01:00
4c0eb37353 chore(debug): remove left-over debug print statement 2021-10-10 18:25:36 +01:00
b81e101ca7 style(lint): Renamed unused function arguments to _ 2021-10-10 18:25:35 +01:00
c760ffa25e Merge pull request #55 from puzza007/patch-1
Fix typo in log message
2021-08-30 10:11:58 +01:00
Paul Oliver
931e6ddf9e Fix typo in log message 2021-08-26 16:00:12 +12:00
f17f485b9f chore(release): 0.6.15 2021-08-05 02:38:15 +01:00
3622df550c fix(build): another --relink-eln-files flag fix
I probably should not be doing these kind of changes after midnight
as I get too lazy to test it as it takes like 20 minutes to do a build
locally :P
2021-08-05 02:37:01 +01:00
2a9707ec89 chore(release): 0.6.14 2021-08-05 01:20:13 +01:00
1fc7faac1f fix(build): silly typo 2021-08-05 01:19:39 +01:00
786d253df6 chore(plan): do not modify archive behavior based on plan disk image
This allows archive creation (or not) to still be controlled via the
--[no-]archive flag.
2021-08-05 00:21:17 +01:00
228ae0939c chore(release): 0.6.13 2021-08-04 23:59:58 +01:00
ac943c430c fix(native_comp): add option to enable/disable relinking *.eln files
Relinking the /usr/local/lib/gcc/10/libgcc_s.1.dylib shared library
within bundled *.eln files is still causing issues with code signing, so
I'm adding an option to toggle *.eln file re-linking on/off, with it on
by default.
2021-08-04 23:58:27 +01:00
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
25 changed files with 1228 additions and 313 deletions

View File

@@ -75,6 +75,9 @@ issues:
- source: "`json:"
linters:
- lll
- source: "`yaml:"
linters:
- lll
run:
skip-dirs:

View File

@@ -2,6 +2,171 @@
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.23](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.22...v0.6.23) (2021-11-10)
### Bug Fixes
* **build:** do not re-link eln files by default ([d338c13](https://github.com/jimeh/build-emacs-for-macos/commit/d338c136db8acc4378154cf66ed7db5462787602)), closes [#60](https://github.com/jimeh/build-emacs-for-macos/issues/60)
### [0.6.22](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.21...v0.6.22) (2021-11-10)
### Features
* **patch:** add support for posix-spawn patch from emacs-plus ([4030ceb](https://github.com/jimeh/build-emacs-for-macos/commit/4030ceb9cab6749af3c28161ac97caec90a8aed0))
### [0.6.21](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.20...v0.6.21) (2021-10-27)
### [0.6.20](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.19...v0.6.20) (2021-10-23)
### Bug Fixes
* **notarization:** explicitly only copy *.c and *.h C source files ([591c39e](https://github.com/jimeh/build-emacs-for-macos/commit/591c39e629c9556adcf296cd5c15dd0b17c4d986))
### [0.6.19](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.18...v0.6.19) (2021-10-23)
### Bug Fixes
* **patch:** resolve emacs-29 symlink patches to their real URL ([bcbd017](https://github.com/jimeh/build-emacs-for-macos/commit/bcbd01778d416b99205c51f348a543489889f66d))
### [0.6.18](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.17...v0.6.18) (2021-10-23)
### Features
* **docs:** embed C source files and set source-directory accordingly ([67b8c5f](https://github.com/jimeh/build-emacs-for-macos/commit/67b8c5f3974e178e31519846d46af82d9770ad6e))
### Bug Fixes
* **patches:** correctly use emacs 28.x and 29.x patches ([4dad581](https://github.com/jimeh/build-emacs-for-macos/commit/4dad5812fa2b67adc7262a778829013995a904bc))
### [0.6.17](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.16...v0.6.17) (2021-10-10)
### Features
* **release:** tweak GitHub release description ([a956dc1](https://github.com/jimeh/build-emacs-for-macos/commit/a956dc1b42b2648e2663f5b48c72d7428fe75f19))
### [0.6.16](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.15...v0.6.16) (2021-10-10)
### Features
* **build:** handle macOS Big Sur and later version number ([2e2f9bc](https://github.com/jimeh/build-emacs-for-macos/commit/2e2f9bc98acdc972a22add3d1015bd80cad20b41))
* **cask:** make cask template helpers more flexible ([d63cd54](https://github.com/jimeh/build-emacs-for-macos/commit/d63cd545aab3a35e0cbbbcabd862525acbc414b8))
* **plan:** allow build plan to be output as YAML or JSON ([1bbfe5d](https://github.com/jimeh/build-emacs-for-macos/commit/1bbfe5d3ea810215b417e5b80d2902f03d68f366))
* **release:** add description to GitHub Releases ([7118ed8](https://github.com/jimeh/build-emacs-for-macos/commit/7118ed856053de06ddcdfba2a2d6fa40f58c17ab))
* **release:** force-replace existing asset files by default ([e7a991e](https://github.com/jimeh/build-emacs-for-macos/commit/e7a991ef92a5c546106a8badaf9c60247a1397b5))
### Bug Fixes
* **release:** publish arguments are not handled as asset files to upload ([b4c5184](https://github.com/jimeh/build-emacs-for-macos/commit/b4c5184cefe43fdc54b1ad5c8a1970f104137644))
### [0.6.15](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.14...v0.6.15) (2021-08-05)
### Bug Fixes
* **build:** another --relink-eln-files flag fix ([3622df5](https://github.com/jimeh/build-emacs-for-macos/commit/3622df550c72fc9da70235005239b278b5822cf6))
### [0.6.14](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.13...v0.6.14) (2021-08-05)
### Bug Fixes
* **build:** silly typo ([1fc7faa](https://github.com/jimeh/build-emacs-for-macos/commit/1fc7faac1f040466fa4474a873d2290273780ee2))
### [0.6.13](https://github.com/jimeh/build-emacs-for-macos/compare/v0.6.12...v0.6.13) (2021-08-04)
### Bug Fixes
* **native_comp:** add option to enable/disable relinking *.eln files ([ac943c4](https://github.com/jimeh/build-emacs-for-macos/commit/ac943c430c58e0761ac44e8d25d4d55a461d01a2))
### [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)
@@ -76,8 +81,10 @@ Options:
--[no-]native-comp Enable/disable native-comp (default: enabled if supported)
--[no-]native-march Enable/disable -march=native CFLAG(default: disabled)
--[no-]native-full-aot Enable/disable NATIVE_FULL_AOT / Ahead of Time compilation (default: disabled)
--[no-]relink-eln-files Enable/disable re-linking shared libraries in bundled *.eln files (default: disabled)
--[no-]rsvg Enable/disable SVG image support via librsvg (default: enabled)
--no-titlebar Apply no-titlebar patch (default: disabled)
--posix-spawn Apply posix-spawn patch (default: disabled)
--no-frame-refocus Apply no-frame-refocus patch (default: disabled)
--[no-]github-auth Make authenticated GitHub API requests if GITHUB_TOKEN environment variable is set.(default: enabled)
--work-dir DIR Specify a working directory where tarballs, sources, and builds will be stored and worked with
@@ -85,7 +92,8 @@ Options:
--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)
--[no-]archive Enable/disable creating *.tbz archive (default: enabled)
--[no-]archive-keep Enable/disable keeping source folder for archive (default: disabled)
--[no-]archive-keep-build-dir
Enable/disable keeping source folder for archive (default: disabled)
--plan FILE Follow given plan file, instead of using given git ref/sha
```

View File

@@ -11,6 +11,7 @@ require 'net/http'
require 'optparse'
require 'pathname'
require 'time'
require 'tmpdir'
require 'uri'
require 'yaml'
@@ -52,7 +53,7 @@ class OSVersion
end
def to_s
@to_s ||= "#{major}.#{minor}"
@to_s ||= major >= 11 ? major.to_s : "#{major}.#{minor}"
end
def major
@@ -104,9 +105,10 @@ class Build
build_dir, app = create_build_dir(app)
handle_native_lisp(app)
add_cli_helper(app)
LibEmbedder.new(app, brew_dir, extra_libs).embed
CLIHelperEmbedder.new(app).embed
CSourcesEmbedder.new(app, @source_dir).embed
LibEmbedder.new(app, brew_dir, extra_libs, options[:relink_eln]).embed
GccLibEmbedder.new(app, gcc_info).embed if options[:native_comp]
archive_build(build_dir) if options[:archive]
@@ -117,9 +119,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')
}
@@ -131,10 +134,6 @@ class Build
@archive_filename = plan.dig('output', 'archive')
end
if plan.dig('output', 'disk_image') || !plan.dig('output', 'archive')
options[:archive] = false
end
@build_name = plan.dig('build', 'name') if plan.dig('build', 'name')
end
@@ -155,11 +154,22 @@ class Build
end
def extra_libs
@extra_libs ||= [
return @extra_libs if @extra_libs
libs = [
File.join(brew_dir, 'opt/expat/lib/libexpat.1.dylib'),
File.join(brew_dir, 'opt/libiconv/lib/libiconv.2.dylib'),
File.join(brew_dir, 'opt/zlib/lib/libz.1.dylib')
]
if options[:native_comp]
libgcc_s = File.join(
brew_dir, 'lib', 'gcc', gcc_info.major_version, 'libgcc_s.1.dylib'
)
libs << libgcc_s if File.exist?(libgcc_s)
end
@extra_libs = libs
end
def download_tarball(sha)
@@ -276,8 +286,6 @@ class Build
verify_native_comp
gcc_info.verify_libgccjit
apply_native_comp_env_setup_patch(source)
ENV['CFLAGS'] = [
"-I#{File.join(gcc_info.root_dir, 'include')}",
"-I#{File.join(gcc_info.libgccjit_root_dir, 'include')}",
@@ -292,6 +300,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 +421,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 +441,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 +460,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,16 +481,27 @@ class Build
end
end
def add_cli_helper(app)
source = File.join(__dir__, 'helper', 'emacs-cli.bash')
target = File.join(app, 'Contents', 'MacOS', 'bin', 'emacs')
dir = File.dirname(target)
def patch_dump_native_lisp_paths(app, emacs_version, eln_version)
sanitized_emacs_version = emacs_version.gsub('.', '-')
sanitized_eln_version = eln_version.gsub('.', '-')
info "Adding \"emacs\" CLI helper to #{dir}"
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'
FileUtils.mkdir_p(dir)
FileUtils.cp(source, target)
FileUtils.chmod('+w', target)
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 build_name
@@ -514,7 +538,7 @@ class Build
system('tar', '-cjf', archive_filename, build)
if options[:archive_keep] == false
info "Removeing \"#{build}\" directory from #{parent_dir}"
info "Removing \"#{build}\" directory from #{parent_dir}"
FileUtils.rm_rf(build_dir)
end
end
@@ -581,21 +605,6 @@ class Build
system(*args) || err("Exit code: #{$CHILD_STATUS.exitstatus}")
end
def apply_native_comp_env_setup_patch(source)
term = 'native-compile-setup-environment-variables'
file = 'lisp/emacs-lisp/comp.el'
return if `grep '#{term}' '#{file}'`.strip.size.positive?
template = File.read(
File.join(__dir__, 'patches/native-comp-env-setup.diff.erb')
)
patch = ERB.new(template).result(gcc_info.get_binding)
patch_file = File.join(source, 'macos_patches/native-comp-env-setup.diff')
File.write(patch_file, patch)
apply_patch({ file: patch_file }, source)
end
def effective_version
@effective_version ||= begin
case ref
@@ -603,8 +612,10 @@ class Build
'emacs-26'
when /^emacs-27.*/
'emacs-27'
else
when /^emacs-28.*/
'emacs-28'
else
'emacs-29'
end
end
end
@@ -612,14 +623,14 @@ class Build
def patches(opts = {})
p = []
if %w[emacs-26 emacs-27 emacs-28].include?(effective_version)
if %w[emacs-26 emacs-27 emacs-28 emacs-29].include?(effective_version)
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
"patches/#{effective_version}/fix-window-role.patch"
}
end
if %w[emacs-27 emacs-28].include?(effective_version)
if %w[emacs-27 emacs-28 emacs-29].include?(effective_version)
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
"patches/#{effective_version}/system-appearance.patch"
@@ -640,6 +651,15 @@ class Build
end
end
if %w[emacs-28 emacs-29].include?(effective_version)
if options[:posix_spawn]
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
"patches/#{effective_version}/posix-spawn.patch"
}
end
end
if effective_version == 'emacs-27'
p << {
url: 'https://github.com/d12frosted/homebrew-emacs-plus/raw/master/' \
@@ -677,7 +697,14 @@ class Build
info "Downloading patch: #{patch[:url]}"
run_cmd('curl', '-L#', patch[:url], '-o', patch_file)
apply_patch({ file: patch_file }, target)
real_patch_url = detect_github_symlink_patch(patch[:url], patch_file)
if real_patch_url
FileUtils.rm(patch_file)
apply_patch({ url: real_patch_url }, target)
else
apply_patch({ file: patch_file }, target)
end
elsif patch[:replace]
err 'Patch replace input error' unless patch[:replace].size == 3
@@ -695,6 +722,30 @@ class Build
f.close
end
end
# When downloading raw files from GitHub, if the target file is a symlink, it
# will return the actual target path of the symlink instead of the content of
# the target file. Hence we have to check if the patch file we have downloaded
# contains one and only one line, and if so, assume it's a symlink.
def detect_github_symlink_patch(original_url, patch_file)
lines = []
# read first two lines
File.open(patch_file) do |f|
lines << f.gets
lines << f.gets
end
# if the file contains more than one line of text, it's not a symlink.
return unless lines[1].nil?
symlink_target = lines[0].strip
# Assume patch file content is something along the lines of
# "../emacs-28/fix-window-role.patch", hence we resolve it relative to the
# original url.
info "patch is symlink to #{symlink_target}"
URI.join(original_url, symlink_target).to_s
end
end
class AbstractEmbedder
@@ -708,8 +759,6 @@ class AbstractEmbedder
@app = app
end
private
def invocation_dir
File.join(app, 'Contents', 'MacOS')
end
@@ -718,20 +767,84 @@ class AbstractEmbedder
File.join(invocation_dir, 'Emacs')
end
def bin_dir
File.join(invocation_dir, 'bin')
end
def lib_dir
File.join(invocation_dir, 'lib')
end
def resources_dir
File.join(app, 'Contents', 'Resources')
end
end
class CLIHelperEmbedder < AbstractEmbedder
def embed
source = File.join(__dir__, 'helper', 'emacs-cli.bash')
target = File.join(bin_dir, 'emacs')
dir = File.dirname(target)
info "Adding \"emacs\" CLI helper to #{dir}"
FileUtils.mkdir_p(dir)
FileUtils.cp(source, target)
FileUtils.chmod('+w', target)
end
end
class CSourcesEmbedder < AbstractEmbedder
PATH_PATCH = <<~ELISP
;; Allow Emacs to find bundled C sources.
(setq source-directory (expand-file-name ".."))
ELISP
attr_reader :source_dir
def initialize(app, source_dir)
super(app)
@source_dir = source_dir
end
def embed
info 'Embedding C source files into Emacs.app for documentation purposes'
src_dir = File.join(source_dir, 'src')
Dir[File.join(src_dir, '**', '*.{c,h}')].each do |f|
rel = f[src_dir.size + 1..-1]
target = File.join(resources_dir, 'src', rel)
FileUtils.mkdir_p(File.dirname(target))
FileUtils.cp(f, target)
end
return if File.exist?(site_start_el_file) &&
File.read(site_start_el_file).include?(PATH_PATCH)
File.open(site_start_el_file, 'a') do |f|
f.puts("\n#{PATH_PATCH}")
end
end
private
def site_start_el_file
@site_start_el_file ||= File.join(resources_dir, 'lisp', 'site-start.el')
end
end
class LibEmbedder < AbstractEmbedder
attr_reader :lib_source
attr_reader :extra_libs
attr_reader :embed_eln_files
def initialize(app, lib_source, extra_libs = [])
def initialize(app, lib_source, extra_libs = [], embed_eln_files = true)
super(app)
@lib_source = lib_source
@extra_libs = extra_libs
@embed_eln_files = embed_eln_files
end
def embed
@@ -749,7 +862,7 @@ class LibEmbedder < AbstractEmbedder
rel_path = Pathname.new(lib_dir).relative_path_from(
Pathname.new(File.dirname(binary))
).to_s
eln_files.each { |f| copy_libs(f, rel_path) }
eln_files.each { |f| copy_libs(f, rel_path) } if embed_eln_files
end
end
end
@@ -757,19 +870,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)
@@ -866,24 +967,59 @@ class GccLibEmbedder < AbstractEmbedder
FileUtils.rm(Dir[File.join(target_dir, '**', '.DS_Store')], force: true)
FileUtils.chmod_R('u+w', target_dir)
FileUtils.mv(source_darwin_dir, target_darwin_dir)
env_setup = ERB.new(NATIVE_COMP_ENV_VAR_TPL).result(gcc_info.get_binding)
return if File.exist?(site_start_el_file) &&
File.read(site_start_el_file).include?(env_setup)
File.open(site_start_el_file, 'a') do |f|
f.puts("\n#{env_setup}")
end
end
private
NATIVE_COMP_ENV_VAR_TPL = <<~ELISP
;; Set LIBRARY_PATH to point at bundled GCC and Xcode Command Line Tools to
;; ensure native-comp works.
(when (and (eq system-type 'darwin)
(string-match-p "\\.app\\/Contents\\/MacOS\\/?$"
invocation-directory))
(let* ((library-path-env (getenv "LIBRARY_PATH"))
(devtools-dir
"/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib")
(gcc-dir (expand-file-name
"<%= app_bundle_relative_lib_dir %>"
invocation-directory))
(darwin-dir (expand-file-name
"<%= app_bundle_relative_darwin_lib_dir %>"
invocation-directory))
(lib-paths (list)))
(if library-path-env
(push library-path-env lib-paths))
(if (file-directory-p devtools-dir)
(push devtools-dir lib-paths))
(push darwin-dir lib-paths)
(push gcc-dir lib-paths)
(setenv "LIBRARY_PATH" (mapconcat 'identity lib-paths ":"))))
ELISP
def embedded?
Dir[File.join(target_dir, 'libgcc*')].any?
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
@@ -893,6 +1029,10 @@ class GccLibEmbedder < AbstractEmbedder
def relative_dir(path, root)
Pathname.new(path).relative_path_from(Pathname.new(root)).to_s
end
def site_start_el_file
@site_start_el_file ||= File.join(resources_dir, 'lisp', 'site-start.el')
end
end
class GccInfo
@@ -939,7 +1079,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 +1140,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
@@ -995,6 +1153,7 @@ if __FILE__ == $PROGRAM_NAME
cli_options = {
work_dir: File.expand_path(__dir__),
native_full_aot: false,
relink_eln: false,
native_march: false,
parallel: Etc.nprocessors,
rsvg: true,
@@ -1052,6 +1211,12 @@ if __FILE__ == $PROGRAM_NAME
cli_options[:native_full_aot] = v
end
opts.on('--[no-]relink-eln-files',
'Enable/disable re-linking shared libraries in bundled *.eln ' \
'files (default: disabled)') do |v|
cli_options[:relink_eln] = v
end
opts.on('--[no-]rsvg',
'Enable/disable SVG image support via librsvg ' \
'(default: enabled)') do |v|
@@ -1062,6 +1227,10 @@ if __FILE__ == $PROGRAM_NAME
cli_options[:no_titlebar] = true
end
opts.on('--posix-spawn', 'Apply posix-spawn patch (default: disabled)') do
cli_options[:posix_spawn] = true
end
opts.on('--no-frame-refocus',
'Apply no-frame-refocus patch (default: disabled)') do
cli_options[:no_frame_refocus] = true

View File

@@ -1,57 +0,0 @@
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index 8c638312b0..87af889ef4 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -4215,6 +4215,52 @@ native-compile-async
(let ((load (not (not load))))
(native--compile-async files recursively load selector)))
+;;;###autoload
+(defun native-compile-setup-environment-variables (&rest _args)
+ "Ensure LIBRARY_PATH is set correctly when libgccjit is bundled."
+ (when (and (eq system-type 'darwin)
+ (string-match-p "\.app\/Contents\/MacOS\/?$"
+ invocation-directory))
+ (let* ((library-path-env (getenv "LIBRARY_PATH"))
+ (devtools-dir
+ "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib")
+ (gcc-dir (expand-file-name
+ "<%= relative_lib_dir %>"
+ invocation-directory))
+ (darwin-dir (expand-file-name
+ "<%= sanitized_relative_darwin_lib_dir %>"
+ invocation-directory))
+ (lib-paths (list)))
+
+ (if library-path-env
+ (push library-path-env lib-paths))
+ (if (file-directory-p devtools-dir)
+ (push devtools-dir lib-paths))
+ (push darwin-dir lib-paths)
+ (push gcc-dir lib-paths)
+
+ (setenv "LIBRARY_PATH" (mapconcat 'identity lib-paths ":"))))
+
+ ;; Remove advice, as it only needs to run once.
+ (advice-remove 'native-compile
+ 'native-compile-setup-environment-variables)
+ (advice-remove 'comp--native-compile
+ 'native-compile-setup-environment-variables)
+ (advice-remove 'native-compile-async
+ 'native-compile-setup-environment-variables)
+ (advice-remove 'native--compile-async
+ 'native-compile-setup-environment-variables))
+
+;; Ensure environment setup runs before any native compilation.
+(advice-add 'native-compile :before
+ 'native-compile-setup-environment-variables)
+(advice-add 'comp--native-compile :before
+ 'native-compile-setup-environment-variables)
+(advice-add 'native-compile-async :before
+ 'native-compile-setup-environment-variables)
+(advice-add 'native--compile-async :before
+ 'native-compile-setup-environment-variables)
+
(provide 'comp)
;; LocalWords: limplified limplified limplification limplify Limple LIMPLE libgccjit elc eln

View File

@@ -11,9 +11,11 @@ type ReleaseInfo struct {
Assets map[string]*ReleaseAsset
}
func (s *ReleaseInfo) Asset(nameMatch string) *ReleaseAsset {
if a, ok := s.Assets[nameMatch]; ok {
return a
func (s *ReleaseInfo) Asset(needles ...string) *ReleaseAsset {
if len(needles) == 1 {
if a, ok := s.Assets[needles[0]]; ok {
return a
}
}
// Dirty and inefficient way to ensure assets are searched in a predictable
@@ -27,16 +29,20 @@ func (s *ReleaseInfo) Asset(nameMatch string) *ReleaseAsset {
})
for _, a := range assets {
if strings.Contains(a.Filename, nameMatch) {
return a
for _, needle := range needles {
if !strings.Contains(a.Filename, needle) {
continue
}
}
return a
}
return nil
}
func (s *ReleaseInfo) DownloadURL(nameMatch string) string {
a := s.Asset(nameMatch)
func (s *ReleaseInfo) DownloadURL(needles ...string) string {
a := s.Asset(needles...)
if a == nil {
return ""
}
@@ -44,8 +50,8 @@ func (s *ReleaseInfo) DownloadURL(nameMatch string) string {
return a.DownloadURL
}
func (s *ReleaseInfo) SHA256(nameMatch string) string {
a := s.Asset(nameMatch)
func (s *ReleaseInfo) SHA256(needles ...string) string {
a := s.Asset(needles...)
if a == nil {
return ""
}

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),
}
@@ -104,7 +112,7 @@ func caskUpdateCmd() *cli2.Command {
func caskUpdateAction(
c *cli2.Context,
opts *Options,
_ *Options,
cOpts *caskOptions,
) error {
updateOpts := &cask.UpdateOptions{
@@ -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

@@ -46,6 +46,7 @@ func New(version, commit, date string) *CLI {
Commands: []*cli2.Command{
planCmd(),
signCmd(),
signFilesCmd(),
notarizeCmd(),
packageCmd(),
releaseCmd(),

View File

@@ -52,7 +52,7 @@ func notarizeCmd() *cli2.Command {
}
}
func notarizeAction(c *cli2.Context, opts *Options) error {
func notarizeAction(c *cli2.Context, _ *Options) error {
options := &notarize.Options{
File: c.Args().Get(0),
BundleID: c.String("bundle-id"),

View File

@@ -1,6 +1,7 @@
package cli
import (
"fmt"
"os"
"path/filepath"
@@ -37,6 +38,12 @@ func planCmd() *cli2.Command {
Name: "sha",
Usage: "override commit SHA of specified git branch/tag",
},
&cli2.StringFlag{
Name: "format",
Aliases: []string{"f"},
Usage: "output format of build plan (yaml or json)",
Value: "yaml",
},
&cli2.StringFlag{
Name: "output",
Usage: "output filename to write plan to instead of printing " +
@@ -89,7 +96,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
}
@@ -102,7 +109,18 @@ func planAction(c *cli2.Context, opts *Options) error {
return err
}
planYAML, err := p.YAML()
format := c.String("format")
var plan string
switch format {
case "yaml", "yml":
format = "yaml"
plan, err = p.YAML()
case "json":
format = "json"
plan, err = p.JSON()
default:
err = fmt.Errorf("--format must be yaml or json")
}
if err != nil {
return err
}
@@ -111,7 +129,7 @@ func planAction(c *cli2.Context, opts *Options) error {
out = os.Stdout
if f := c.String("output"); f != "" {
logger.Info("writing plan", "file", f)
logger.Debug("content", "yaml", planYAML)
logger.Debug("content", format, plan)
out, err = os.Create(f)
if err != nil {
return err
@@ -119,7 +137,7 @@ func planAction(c *cli2.Context, opts *Options) error {
defer out.Close()
}
_, err = out.WriteString(planYAML)
_, err = out.WriteString(plan)
if err != nil {
return err
}

View File

@@ -1,6 +1,7 @@
package cli
import (
"errors"
"fmt"
"os"
"path/filepath"
@@ -42,7 +43,7 @@ func releaseCmd() *cli2.Command {
Usage: "owner/name of GitHub repo to check for release, " +
"ignored if a plan is provided",
EnvVars: []string{"GITHUB_REPOSITORY"},
Value: "jimeh/emacs-builds",
Value: "",
},
&cli2.StringFlag{
Name: "name",
@@ -60,6 +61,7 @@ func releaseCmd() *cli2.Command {
Subcommands: []*cli2.Command{
releaseCheckCmd(),
releasePublishCmd(),
releaseBulkCmd(),
},
}
}
@@ -106,7 +108,7 @@ func releaseCheckCmd() *cli2.Command {
func releaseCheckAction(
c *cli2.Context,
opts *Options,
_ *Options,
rOpts *releaseOptions,
) error {
rlsOpts := &release.CheckOptions{
@@ -151,6 +153,12 @@ func releasePublishCmd() *cli2.Command {
"specified",
Value: "",
},
&cli2.BoolFlag{
Name: "asset-size-check",
Usage: "Do not replace existing asset files if local and " +
"remote file sizes match.",
Value: false,
},
},
Action: releaseActionWrapper(releasePublishAction),
}
@@ -158,16 +166,17 @@ func releasePublishCmd() *cli2.Command {
func releasePublishAction(
c *cli2.Context,
opts *Options,
_ *Options,
rOpts *releaseOptions,
) error {
rlsOpts := &release.PublishOptions{
Repository: rOpts.Repository,
CommitRef: c.String("release-sha"),
ReleaseName: rOpts.Name,
ReleaseTitle: c.String("title"),
AssetFiles: c.Args().Slice(),
GithubToken: rOpts.GithubToken,
Repository: rOpts.Repository,
CommitRef: c.String("release-sha"),
ReleaseName: rOpts.Name,
ReleaseTitle: c.String("title"),
AssetFiles: c.Args().Slice(),
AssetSizeCheck: c.Bool("asset-size-check"),
GithubToken: rOpts.GithubToken,
}
rlsType := c.String("type")
@@ -182,7 +191,13 @@ func releasePublishAction(
return fmt.Errorf("invalid --type \"%s\"", rlsType)
}
if c.Args().Len() > 0 {
rlsOpts.AssetFiles = c.Args().Slice()
}
if rOpts.Plan != nil {
rlsOpts.Source = rOpts.Plan.Source
if rOpts.Plan.Release != nil {
rlsOpts.ReleaseName = rOpts.Plan.Release.Name
rlsOpts.ReleaseTitle = rOpts.Plan.Release.Title
@@ -194,7 +209,8 @@ func releasePublishAction(
}
}
if rOpts.Plan.Output != nil {
// Set asset files based on plan if no file arguments were given.
if len(rlsOpts.AssetFiles) == 0 && rOpts.Plan.Output != nil {
rlsOpts.AssetFiles = []string{
filepath.Join(
rOpts.Plan.Output.Directory,
@@ -206,3 +222,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,
_ *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

@@ -112,3 +112,49 @@ func signAction(c *cli2.Context, opts *Options) error {
return sign.Emacs(c.Context, app, signOpts)
}
func signFilesCmd() *cli2.Command {
signCmd := signCmd()
var flags []cli2.Flag
for _, f := range signCmd.Flags {
n := f.Names()
if len(n) > 0 && n[0] == "plan" {
continue
}
flags = append(flags, f)
}
return &cli2.Command{
Name: "sign-files",
Usage: "sign files with codesign",
ArgsUsage: "<file> [<file>...]",
Hidden: true,
Flags: flags,
Action: actionWrapper(signFilesAction),
}
}
func signFilesAction(c *cli2.Context, opts *Options) error {
signOpts := &sign.Options{
Identity: c.String("sign"),
Options: c.StringSlice("options"),
Deep: c.Bool("deep"),
Timestamp: c.Bool("timestamp"),
Force: c.Bool("force"),
Verbose: c.Bool("verbose"),
CodeSignCmd: c.String("codesign"),
}
if v := c.StringSlice("entitlements"); len(v) > 0 {
e := sign.Entitlements(v)
signOpts.Entitlements = &e
}
if !opts.quiet {
signOpts.Output = os.Stdout
}
return sign.Files(c.Context, c.Args().Slice(), signOpts)
}

View File

@@ -8,11 +8,11 @@ import (
)
type Commit struct {
SHA string `yaml:"sha"`
Date *time.Time `yaml:"date"`
Author string `yaml:"author"`
Committer string `yaml:"committer"`
Message string `yaml:"message"`
SHA string `yaml:"sha" json:"sha"`
Date *time.Time `yaml:"date" json:"date"`
Author string `yaml:"author" json:"author"`
Committer string `yaml:"committer" json:"committer"`
Message string `yaml:"message" json:"message"`
}
func New(rc *github.RepositoryCommit) *Commit {

View File

@@ -2,13 +2,14 @@ package osinfo
import (
"os/exec"
"strconv"
"strings"
)
type OSInfo struct {
Name string `yaml:"name"`
Version string `yaml:"version"`
Arch string `yaml:"arch"`
Name string `yaml:"name" json:"name"`
Version string `yaml:"version" json:"version"`
Arch string `yaml:"arch" json:"arch"`
}
func New() (*OSInfo, error) {
@@ -29,8 +30,17 @@ func New() (*OSInfo, error) {
}, nil
}
func (s *OSInfo) MajorMinor() string {
// DistinctVersion returns macOS version down to a distinct "major"
// version. For macOS 10.x, this will include the first two numeric parts of the
// version (10.15), while for 11.x and later, the first numeric part is enough
// (11).
func (s *OSInfo) DistinctVersion() string {
parts := strings.Split(s.Version, ".")
if n, _ := strconv.Atoi(parts[0]); n >= 11 {
return parts[0]
}
max := len(parts)
if max > 2 {
max = 2

View File

@@ -11,7 +11,9 @@ 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"
"github.com/jimeh/build-emacs-for-macos/pkg/source"
)
var nonAlphaNum = regexp.MustCompile(`[^\w_-]+`)
@@ -64,16 +66,18 @@ 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,
sanitizeString(osInfo.Name+"-"+osInfo.MajorMinor()),
"Emacs.%s.%s.%s",
version,
sanitizeString(osInfo.Name+"-"+osInfo.DistinctVersion()),
sanitizeString(osInfo.Arch),
)
diskImage := buildName + ".dmg"
@@ -82,17 +86,18 @@ func Create(ctx context.Context, opts *Options) (*Plan, error) {
Build: &Build{
Name: buildName,
},
Source: &Source{
Source: &source.Source{
Ref: opts.Ref,
Repository: repo,
Commit: commitInfo,
Tarball: &Tarball{
Tarball: &source.Tarball{
URL: repo.TarballURL(commitInfo.SHA),
},
},
OS: osInfo,
Release: &Release{
Name: releaseName,
Name: releaseName,
Prerelease: true,
},
Output: &Output{
Directory: opts.OutputDir,
@@ -100,16 +105,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, ".")

View File

@@ -2,21 +2,21 @@ package plan
import (
"bytes"
"encoding/json"
"io"
"os"
"github.com/jimeh/build-emacs-for-macos/pkg/commit"
"github.com/jimeh/build-emacs-for-macos/pkg/osinfo"
"github.com/jimeh/build-emacs-for-macos/pkg/repository"
"github.com/jimeh/build-emacs-for-macos/pkg/source"
"gopkg.in/yaml.v3"
)
type Plan struct {
Build *Build `yaml:"build,omitempty"`
Source *Source `yaml:"source,omitempty"`
OS *osinfo.OSInfo `yaml:"os,omitempty"`
Release *Release `yaml:"release,omitempty"`
Output *Output `yaml:"output,omitempty"`
Build *Build `yaml:"build,omitempty" json:"build,omitempty"`
Source *source.Source `yaml:"source,omitempty" json:"source,omitempty"`
OS *osinfo.OSInfo `yaml:"os,omitempty" json:"os,omitempty"`
Release *Release `yaml:"release,omitempty" json:"release,omitempty"`
Output *Output `yaml:"output,omitempty" json:"output,omitempty"`
}
// Load attempts to loads a plan YAML from given filename.
@@ -54,29 +54,37 @@ func (s *Plan) YAML() (string, error) {
return buf.String(), nil
}
// WriteJSON writes plan in JSON format to given io.Writer.
func (s *Plan) WriteJSON(w io.Writer) error {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
return enc.Encode(s)
}
// JSON returns plan in JSON format.
func (s *Plan) JSON() (string, error) {
var buf bytes.Buffer
err := s.WriteJSON(&buf)
if err != nil {
return "", err
}
return buf.String(), nil
}
type Build struct {
Name string `yaml:"name,omitempty"`
}
type Source struct {
Ref string `yaml:"ref,omitempty"`
Repository *repository.Repository `yaml:"repository,omitempty"`
Commit *commit.Commit `yaml:"commit,omitempty"`
Tarball *Tarball `yaml:"tarball,omitempty"`
}
type Tarball struct {
URL string `yaml:"url,omitempty"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
}
type Release struct {
Name string `yaml:"name"`
Title string `yaml:"title,omitempty"`
Draft bool `yaml:"draft,omitempty"`
Prerelease bool `yaml:"prerelease,omitempty"`
Name string `yaml:"name" json:"name"`
Title string `yaml:"title,omitempty" json:"title,omitempty"`
Draft bool `yaml:"draft,omitempty" json:"draft,omitempty"`
Prerelease bool `yaml:"prerelease,omitempty" json:"prerelease,omitempty"`
}
type Output struct {
Directory string `yaml:"directory,omitempty"`
DiskImage string `yaml:"disk_image,omitempty"`
Directory string `yaml:"directory,omitempty" json:"directory,omitempty"`
DiskImage string `yaml:"disk_image,omitempty" json:"disk_image,omitempty"`
}

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
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/hashicorp/go-hclog"
"github.com/jimeh/build-emacs-for-macos/pkg/gh"
"github.com/jimeh/build-emacs-for-macos/pkg/repository"
"github.com/jimeh/build-emacs-for-macos/pkg/source"
)
type releaseType int
@@ -40,15 +41,26 @@ type PublishOptions struct {
// draft)
ReleaseType releaseType
// Source contains the source used to build the asset files. When set a
// release body/description text will be generated based on source commit
// details.
Source *source.Source
// AssetFiles is a list of files which must all exist in the release for
// the check to pass.
AssetFiles []string
// AssetSizeCheck causes a file size check for any existing asset files on a
// release which have the same filename as a asset we want to upload. If the
// size of the local and remote files are the same, the existing asset file
// is left in place. When this is false, given asset files will always be
// uploaded, replacing any asset files with the same filename.
AssetSizeCheck bool
// GitHubToken is the OAuth token used to talk to the GitHub API.
GithubToken string
}
//nolint:funlen,gocyclo
// Publish creates and publishes a GitHub release.
func Publish(ctx context.Context, opts *PublishOptions) error {
logger := hclog.FromContext(ctx).Named("release")
@@ -68,6 +80,16 @@ func Publish(ctx context.Context, opts *PublishOptions) error {
prerelease := opts.ReleaseType == Prerelease
draft := opts.ReleaseType == Draft
body := ""
if opts.Source != nil {
body, err = releaseBody(opts)
if err != nil {
return err
}
logger.Debug("rendered release body", "content", body)
}
created := false
logger.Info("checking release", "tag", tagName)
release, resp, err := gh.Repositories.GetReleaseByTag(
ctx, opts.Repository.Owner(), opts.Repository.Name(), tagName,
@@ -77,6 +99,7 @@ func Publish(ctx context.Context, opts *PublishOptions) error {
return err
}
created = true
logger.Info("creating release", "tag", tagName, "name", name)
release, _, err = gh.Repositories.CreateRelease(
@@ -87,6 +110,7 @@ func Publish(ctx context.Context, opts *PublishOptions) error {
TargetCommitish: &opts.CommitRef,
Prerelease: boolPtr(false),
Draft: boolPtr(true),
Body: &body,
},
)
if err != nil {
@@ -94,62 +118,9 @@ func Publish(ctx context.Context, opts *PublishOptions) error {
}
}
for _, fileName := range files {
fileIO, err2 := os.Open(fileName)
if err2 != nil {
return err2
}
defer fileIO.Close()
fileInfo, err2 := fileIO.Stat()
if err2 != nil {
return err2
}
fileBaseName := filepath.Base(fileName)
assetExists := false
for _, a := range release.Assets {
if a.GetName() != fileBaseName {
continue
}
if a.GetSize() == int(fileInfo.Size()) {
logger.Info("asset exists with correct size",
"file", fileBaseName,
"local_size", byteCountIEC(fileInfo.Size()),
"remote_size", byteCountIEC(int64(a.GetSize())),
)
assetExists = true
} else {
_, err = gh.Repositories.DeleteReleaseAsset(
ctx, opts.Repository.Owner(), opts.Repository.Name(),
a.GetID(),
)
if err != nil {
return err
}
logger.Info(
"deleted asset with wrong size", "file", fileBaseName,
)
}
}
if !assetExists {
logger.Info("uploading asset",
"file", fileBaseName,
"size", byteCountIEC(fileInfo.Size()),
)
_, _, err2 = gh.Repositories.UploadReleaseAsset(
ctx, opts.Repository.Owner(), opts.Repository.Name(),
release.GetID(),
&github.UploadOptions{Name: fileBaseName},
fileIO,
)
if err2 != nil {
return err2
}
}
err = uploadReleaseAssets(ctx, gh, release, files, opts)
if err != nil {
return err
}
changed := false
@@ -158,6 +129,11 @@ func Publish(ctx context.Context, opts *PublishOptions) error {
changed = true
}
if body != "" && release.GetBody() != body {
release.Body = &body
changed = true
}
if release.GetDraft() != draft {
release.Draft = &draft
changed = true
@@ -169,6 +145,7 @@ func Publish(ctx context.Context, opts *PublishOptions) error {
}
if changed {
logger.Info("updating release attributes", "url", release.GetHTMLURL())
release, _, err = gh.Repositories.EditRelease(
ctx, opts.Repository.Owner(), opts.Repository.Name(),
release.GetID(), release,
@@ -178,13 +155,89 @@ func Publish(ctx context.Context, opts *PublishOptions) error {
}
}
logger.Info("release created", "url", release.GetHTMLURL())
if created {
logger.Info("release created", "url", release.GetHTMLURL())
} else {
logger.Info("release updated", "url", release.GetHTMLURL())
}
return nil
}
func uploadReleaseAssets(
ctx context.Context,
gh *github.Client,
release *github.RepositoryRelease,
fileNames []string,
opts *PublishOptions,
) error {
logger := hclog.FromContext(ctx).Named("release")
for _, fileName := range fileNames {
logger.Debug("processing asset", "file", filepath.Base(fileName))
fileIO, err := os.Open(fileName)
if err != nil {
return err
}
defer fileIO.Close()
fileInfo, err := fileIO.Stat()
if err != nil {
return err
}
fileBaseName := filepath.Base(fileName)
assetExists := false
for _, a := range release.Assets {
if a.GetName() != fileBaseName {
continue
}
if opts.AssetSizeCheck && a.GetSize() == int(fileInfo.Size()) {
logger.Info("asset exists with correct size",
"file", fileBaseName,
"local_size", byteCountIEC(fileInfo.Size()),
"remote_size", byteCountIEC(int64(a.GetSize())),
)
assetExists = true
} else {
logger.Info(
"deleting existing asset", "file", fileBaseName,
)
_, err = gh.Repositories.DeleteReleaseAsset(
ctx, opts.Repository.Owner(), opts.Repository.Name(),
a.GetID(),
)
if err != nil {
return err
}
}
}
if !assetExists {
logger.Info("uploading asset",
"file", fileBaseName,
"size", byteCountIEC(fileInfo.Size()),
)
_, _, err = gh.Repositories.UploadReleaseAsset(
ctx, opts.Repository.Owner(), opts.Repository.Name(),
release.GetID(),
&github.UploadOptions{Name: fileBaseName},
fileIO,
)
if err != nil {
return err
}
}
}
return nil
}
func publishFileList(files []string) ([]string, error) {
var output []string
results := map[string]struct{}{}
for _, file := range files {
var err error
file, err = filepath.Abs(file)
@@ -200,11 +253,10 @@ func publishFileList(files []string) ([]string, error) {
return nil, fmt.Errorf("\"%s\" is not a file", file)
}
output = append(output, file)
results[file] = struct{}{}
sumFile := file + ".sha256"
_, err = os.Stat(sumFile)
fmt.Printf("err: %+v\n", err)
if err != nil {
if os.IsNotExist(err) {
continue
@@ -212,7 +264,12 @@ func publishFileList(files []string) ([]string, error) {
return nil, err
}
output = append(output, sumFile)
results[sumFile] = struct{}{}
}
var output []string
for f := range results {
output = append(output, f)
}
return output, nil

View File

@@ -0,0 +1,78 @@
package release
import (
"bytes"
"os"
"strings"
"text/template"
)
var tplFuncs = template.FuncMap{
"indent": func(n int, s string) string {
pad := strings.Repeat(" ", n)
return pad + strings.ReplaceAll(s, "\n", "\n"+pad)
},
}
var bodyTpl = template.Must(template.New("body").Funcs(tplFuncs).Parse(`
{{- $t := "` + "`" + `" -}}
### Build Details
{{ with .SourceURL -}}
- Source: {{ . }}
{{- end }}
{{- if .CommitURL }}
- Commit: {{ .CommitURL }}
{{- if .CommitSHA }} ({{ $t }}{{ .CommitSHA }}{{ $t }}){{ end }}
{{- end }}
{{- with .TarballURL }}
- Tarball: {{ . }}
{{- end }}
{{- with .BuildLogURL }}
- Build Log: {{ . }} (available for 90 days)
{{- end }}`,
))
type bodyData struct {
SourceURL string
CommitSHA string
CommitURL string
BuildLogURL string
TarballURL string
}
func releaseBody(opts *PublishOptions) (string, error) {
src := opts.Source
if src.Repository == nil || src.Commit == nil {
return "", nil
}
data := &bodyData{
SourceURL: src.Repository.TreeURL(src.Ref),
CommitSHA: src.Commit.SHA,
CommitURL: src.Repository.CommitURL(src.Commit.SHA),
TarballURL: src.Repository.TarballURL(src.Commit.SHA),
}
// If available, use the exact value from the build plan.
if src.Tarball != nil {
data.TarballURL = src.Tarball.URL
}
// If running within GitHub Actions, provide link to build log.
if opts.Repository != nil {
if id := os.Getenv("GITHUB_RUN_ID"); id != "" {
data.BuildLogURL = opts.Repository.ActionRunURL(id)
}
}
var buf bytes.Buffer
err := bodyTpl.Execute(&buf, data)
if err != nil {
return "", err
}
return buf.String(), 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

@@ -22,8 +22,8 @@ const GitHub Type = "github"
// Repository represents basic information about a repository with helper
// methods to get various pieces of information from it.
type Repository struct {
Type Type `yaml:"type,omitempty"`
Source string `yaml:"source,omitempty"`
Type Type `yaml:"type,omitempty" json:"type,omitempty"`
Source string `yaml:"source,omitempty" json:"source,omitempty"`
}
func NewGitHub(ownerAndName string) (*Repository, error) {
@@ -89,3 +89,42 @@ func (s *Repository) TarballURL(ref string) string {
return ""
}
}
func (s *Repository) CommitURL(ref string) string {
if ref == "" {
return ""
}
switch s.Type {
case GitHub:
return GitHubBaseURL + s.Source + "/commit/" + ref
default:
return ""
}
}
func (s *Repository) TreeURL(ref string) string {
if ref == "" {
return ""
}
switch s.Type {
case GitHub:
return GitHubBaseURL + s.Source + "/tree/" + ref
default:
return ""
}
}
func (s *Repository) ActionRunURL(runID string) string {
if runID == "" {
return ""
}
switch s.Type {
case GitHub:
return GitHubBaseURL + s.Source + "/actions/runs/" + runID
default:
return ""
}
}

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

17
pkg/source/source.go Normal file
View File

@@ -0,0 +1,17 @@
package source
import (
"github.com/jimeh/build-emacs-for-macos/pkg/commit"
"github.com/jimeh/build-emacs-for-macos/pkg/repository"
)
type Source struct {
Ref string `yaml:"ref,omitempty" json:"ref,omitempty"`
Repository *repository.Repository `yaml:"repository,omitempty" json:"repository,omitempty"`
Commit *commit.Commit `yaml:"commit,omitempty" json:"commit,omitempty"`
Tarball *Tarball `yaml:"tarball,omitempty" json:"tarball,omitempty"`
}
type Tarball struct {
URL string `yaml:"url,omitempty" json:"url,omitempty"`
}