native-packages 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +86 -0
- data/README.md +52 -121
- data/docs/404.md +12 -0
- data/docs/_guides/apple-notarization.md +116 -0
- data/docs/_guides/building-packages.md +190 -0
- data/docs/_guides/distribution-recipes.md +192 -0
- data/docs/_guides/getting-started.md +184 -0
- data/docs/_guides/github-actions.md +146 -0
- data/docs/_guides/multi-platform.md +153 -0
- data/docs/_guides/native-recipes.md +189 -0
- data/docs/_guides/prereleases.md +72 -0
- data/docs/_guides/publishing.md +143 -0
- data/docs/_guides/release-signing.md +137 -0
- data/docs/_reference/commands.md +206 -0
- data/docs/_reference/configuration.md +323 -0
- data/docs/_reference/platforms.md +104 -0
- data/docs/_reference/troubleshooting.md +172 -0
- data/docs/apple-notarization.md +2 -87
- data/docs/assets/images/logo.svg +27 -0
- data/docs/configuration.md +2 -159
- data/docs/index.md +41 -0
- data/docs/native-recipes.md +3 -134
- data/docs/platforms.md +3 -32
- data/docs/releasing.md +1 -1
- data/examples/native-packages-all-formats.yaml +1 -1
- data/examples/native-packages.yaml +1 -1
- data/lib/native_packages/cli.rb +21 -0
- data/lib/native_packages/release_signing.rb +106 -0
- data/lib/native_packages/support.rb +1 -1
- metadata +22 -3
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: macOS and Windows installers
|
|
3
|
+
description: Connect your DMG or Inno Setup script to the same build and publication process as your Linux packages.
|
|
4
|
+
nav_order: 5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# macOS and Windows installers
|
|
8
|
+
|
|
9
|
+
This guide shows how to connect an existing DMG or Inno Setup script to
|
|
10
|
+
native-packages. You supply a built app and a packaging script. The build
|
|
11
|
+
command checks the app, runs your script, and records the finished installer's
|
|
12
|
+
checksum.
|
|
13
|
+
|
|
14
|
+
Build **DMGs on macOS** and **Inno installers on Windows**. These targets use
|
|
15
|
+
your native tools and do not need an nFPM executable.
|
|
16
|
+
|
|
17
|
+
## Prepare your app
|
|
18
|
+
|
|
19
|
+
Build the complete application first, including any libraries and supporting
|
|
20
|
+
files. A macOS input can be `dist/Hello.app`. A Windows input can be a directory
|
|
21
|
+
such as `dist/windows` containing `hello.exe` and its dependencies.
|
|
22
|
+
|
|
23
|
+
If your inputs come from another job, download and unpack them before packaging.
|
|
24
|
+
Native targets require a local directory and use `build --version`; they do
|
|
25
|
+
not accept `build --release` downloads directly.
|
|
26
|
+
|
|
27
|
+
## Configure the targets
|
|
28
|
+
|
|
29
|
+
Here is a complete configuration for an ARM64 Mac app and an x86-64 Windows app:
|
|
30
|
+
|
|
31
|
+
```yaml
|
|
32
|
+
schema: 1
|
|
33
|
+
tool:
|
|
34
|
+
version: '0.7.0'
|
|
35
|
+
nfpm: '2.47.0'
|
|
36
|
+
nfpm:
|
|
37
|
+
name: hello
|
|
38
|
+
description: A small greeting application
|
|
39
|
+
maintainer: Your Name <you@example.com>
|
|
40
|
+
license: MIT
|
|
41
|
+
release:
|
|
42
|
+
repository: your-name/hello
|
|
43
|
+
targets:
|
|
44
|
+
macos-arm64:
|
|
45
|
+
platform: macos
|
|
46
|
+
arch: arm64
|
|
47
|
+
formats: [dmg]
|
|
48
|
+
input:
|
|
49
|
+
kind: directory
|
|
50
|
+
local: dist/Hello.app
|
|
51
|
+
native:
|
|
52
|
+
command: [ruby, packaging/dmg.rb, '@PAYLOAD@', '@PACKAGE@']
|
|
53
|
+
output: 'hello-@TAG@-macos-arm64.dmg'
|
|
54
|
+
windows-amd64:
|
|
55
|
+
platform: windows
|
|
56
|
+
arch: amd64
|
|
57
|
+
formats: [inno]
|
|
58
|
+
input:
|
|
59
|
+
kind: directory
|
|
60
|
+
local: dist/windows
|
|
61
|
+
native:
|
|
62
|
+
command: [ruby, packaging/setup.rb, '@PAYLOAD@', '@PACKAGE@', '@VERSION@']
|
|
63
|
+
output: 'hello-@TAG@-windows-setup.exe'
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The `nfpm` section still holds shared package metadata, and `tool.nfpm` keeps
|
|
67
|
+
mixed-platform configurations consistent. Native targets use `native.command`
|
|
68
|
+
to create their output.
|
|
69
|
+
|
|
70
|
+
For Intel Macs, use `arch: amd64`. Use `arch: universal` for a build containing
|
|
71
|
+
both Intel and ARM64 code. Architecture checks use Apple's `lipo`.
|
|
72
|
+
|
|
73
|
+
## Write the packaging command
|
|
74
|
+
|
|
75
|
+
`native.command` is a list of arguments. It must include `@PACKAGE@`:
|
|
76
|
+
|
|
77
|
+
| Value | What your script receives |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| `@PAYLOAD@` | A temporary copy of the input to package. |
|
|
80
|
+
| `@PACKAGE@` | The exact, absolute path where the output must be created. |
|
|
81
|
+
| `@VERSION@` | App version, such as `1.2.3`. |
|
|
82
|
+
| `@TAG@` | Release tag, such as `v1.2.3`. |
|
|
83
|
+
|
|
84
|
+
Commands run in the configuration directory. They also receive
|
|
85
|
+
`NATIVE_PACKAGES_TARGET`, `NATIVE_PACKAGES_VERSION`, and `SOURCE_DATE_EPOCH`.
|
|
86
|
+
|
|
87
|
+
### A minimal DMG script
|
|
88
|
+
|
|
89
|
+
For the `Hello.app` input above, create `packaging/dmg.rb`:
|
|
90
|
+
|
|
91
|
+
```ruby
|
|
92
|
+
require "fileutils"
|
|
93
|
+
require "tmpdir"
|
|
94
|
+
|
|
95
|
+
payload, output = ARGV
|
|
96
|
+
abort "expected payload and output paths" unless payload && output
|
|
97
|
+
abort "output already exists" if File.exist?(output)
|
|
98
|
+
|
|
99
|
+
Dir.mktmpdir("hello-dmg-") do |staging|
|
|
100
|
+
FileUtils.cp_r(payload, File.join(staging, "Hello.app"), preserve: true)
|
|
101
|
+
File.symlink("/Applications", File.join(staging, "Applications"))
|
|
102
|
+
success = system("hdiutil", "create", "-volname", "Hello",
|
|
103
|
+
"-srcfolder", staging, "-format", "UDZO", output)
|
|
104
|
+
abort "DMG creation failed" unless success
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
abort "DMG verification failed" unless system("hdiutil", "verify", output)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This places the app and an Applications shortcut inside a compressed disk
|
|
111
|
+
image. Adapt the volume name and app name to your project.
|
|
112
|
+
|
|
113
|
+
### An Inno Setup script
|
|
114
|
+
|
|
115
|
+
Keep your app's `.iss` recipe in the repository. Have `packaging/setup.rb`
|
|
116
|
+
invoke your installed Inno compiler and pass it the three arguments above.
|
|
117
|
+
The wrapper should:
|
|
118
|
+
|
|
119
|
+
1. Use `@PAYLOAD@` as the source directory for the recipe's files.
|
|
120
|
+
2. Map `@VERSION@` to the app and numeric installer version fields.
|
|
121
|
+
3. Set the compiler's output directory and filename from `@PACKAGE@`.
|
|
122
|
+
4. Fail if compilation fails.
|
|
123
|
+
|
|
124
|
+
An Inno recipe also owns your app's identity, installation location, shortcuts,
|
|
125
|
+
and uninstall behavior. See the
|
|
126
|
+
[Inno command-line compiler documentation](https://jrsoftware.org/ishelp/topic_compilercmdline.htm)
|
|
127
|
+
for invoking it from your wrapper.
|
|
128
|
+
|
|
129
|
+
## Build on each host
|
|
130
|
+
|
|
131
|
+
On the Mac:
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
native-packages doctor --target macos-arm64
|
|
135
|
+
native-packages build --version 1.2.3 --target macos-arm64 --output dist/macos-packages
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
On Windows:
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
native-packages doctor --target windows-amd64
|
|
142
|
+
native-packages build --version 1.2.3 --target windows-amd64 --output dist/windows-packages
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`doctor` checks the host and the command's executable. Your script checks its
|
|
146
|
+
additional tools, such as the Inno compiler. `validate` and `build --dry-run`
|
|
147
|
+
do not execute it.
|
|
148
|
+
|
|
149
|
+
The build checks Mach-O or PE architecture, runs the script, and checks that
|
|
150
|
+
it produced exactly the declared DMG or EXE. Test installation, startup,
|
|
151
|
+
upgrades, and removal separately for your app.
|
|
152
|
+
|
|
153
|
+
## Preserve the prepared input
|
|
154
|
+
|
|
155
|
+
Package the copy at `@PAYLOAD@`. A top-level `.app` keeps its basename; other
|
|
156
|
+
input directories are copied into a directory named `payload`.
|
|
157
|
+
|
|
158
|
+
The copy preserves file modes and safe internal relative symlinks, including
|
|
159
|
+
framework links. Dangling links, links escaping the input, and special files
|
|
160
|
+
are rejected. Your recipe and output hooks must leave the copied input
|
|
161
|
+
unchanged. Use your own temporary staging directory when arranging files.
|
|
162
|
+
|
|
163
|
+
The command must create only its declared output in the destination directory.
|
|
164
|
+
Keep logs and temporary files elsewhere.
|
|
165
|
+
|
|
166
|
+
## Sign before recording checksums
|
|
167
|
+
|
|
168
|
+
For DMG targets, providing complete Apple credentials enables automatic input
|
|
169
|
+
signing and DMG notarization. Follow [Apple signing and notarization](apple-notarization.md).
|
|
170
|
+
The DMG script must preserve the signed app when copying it into the image.
|
|
171
|
+
|
|
172
|
+
For Windows, use an `after_package` hook to sign the output:
|
|
173
|
+
|
|
174
|
+
```yaml
|
|
175
|
+
after_package: [pwsh, -File, packaging/sign.ps1, '@PACKAGE@']
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
This runs before the output hash is recorded. It must preserve the output's
|
|
179
|
+
filename and file set. Do not sign or otherwise change the installer after
|
|
180
|
+
building; publication would reject the changed hash.
|
|
181
|
+
|
|
182
|
+
MSIX is a separate Windows format. It uses package metadata and file mappings
|
|
183
|
+
instead of `native.command`; see [Supported platforms](../_reference/platforms.md#msix).
|
|
184
|
+
|
|
185
|
+
## Combine and publish
|
|
186
|
+
|
|
187
|
+
Use [Building across platforms](multi-platform.md) to combine these directories
|
|
188
|
+
with Linux builds. If your configuration also has AUR or Homebrew templates,
|
|
189
|
+
that guide explains how to generate recipes after all native packages exist.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Prereleases
|
|
3
|
+
description: Build and publish alpha, beta, and release candidate packages with explicit version rules.
|
|
4
|
+
nav_order: 9
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Prereleases
|
|
8
|
+
|
|
9
|
+
Use a prerelease to let people test an upcoming version before the stable
|
|
10
|
+
release. native-packages supports alpha, beta, and release candidate packages
|
|
11
|
+
for **DEB, RPM, DMG, and Inno Setup**.
|
|
12
|
+
|
|
13
|
+
## Enable preview versions
|
|
14
|
+
|
|
15
|
+
Add this to your release settings:
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
release:
|
|
19
|
+
repository: your-name/hello
|
|
20
|
+
prereleases: true
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Then build with a version such as:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
native-packages build --version 1.2.3-alpha.1
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Accepted suffixes are `-alpha.N`, `-beta.N`, and `-rc.N`, where `N` is a positive
|
|
30
|
+
integer. Build metadata suffixes such as `+build.5` are not accepted.
|
|
31
|
+
Stable versions such as `1.2.3` continue to work.
|
|
32
|
+
|
|
33
|
+
DEB and RPM use the packaging helper's version conversion; for example,
|
|
34
|
+
`1.2.3-alpha.1` becomes `1.2.3~alpha.1`. Keep the normal SemVer conversion
|
|
35
|
+
enabled and do not override the prerelease field.
|
|
36
|
+
|
|
37
|
+
## Keep distribution recipes for stable releases
|
|
38
|
+
|
|
39
|
+
The simplest setup is a separate preview configuration with supported formats
|
|
40
|
+
and no `templates`:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
native-packages --config native-packages.preview.yaml build \
|
|
44
|
+
--version 1.2.3-beta.1
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
If a native build shares a configuration with stable AUR or Homebrew recipes,
|
|
48
|
+
you can use `--defer-recipes` to produce its preview package. Those recipes
|
|
49
|
+
cannot be finalized for a prerelease, and the deferred build cannot be passed
|
|
50
|
+
to `publish --from`. Collect the native output through your application's
|
|
51
|
+
release job, or use a separate preview configuration for normal CLI publication.
|
|
52
|
+
|
|
53
|
+
## Publish to a GitHub prerelease
|
|
54
|
+
|
|
55
|
+
Create the matching GitHub release, such as `v1.2.3-alpha.1`, and mark it as a
|
|
56
|
+
prerelease. Then publish a complete, non-deferred build:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
native-packages publish --from dist/packages/1.2.3-alpha.1 --to github
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The command checks that the existing release is marked as a prerelease. It
|
|
63
|
+
does not create the release or change its status. Downstream recipe publication
|
|
64
|
+
is reserved for stable versions.
|
|
65
|
+
|
|
66
|
+
## Native installer versions
|
|
67
|
+
|
|
68
|
+
Your DMG or Inno script receives the app version, including the suffix.
|
|
69
|
+
If the installer needs numeric version fields, define that mapping in your
|
|
70
|
+
app's script. Test that alpha 2 upgrades alpha 1 and that the stable version
|
|
71
|
+
upgrades the previews. The packaging tool does not choose that ordering policy
|
|
72
|
+
for your application.
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Publishing a release
|
|
3
|
+
description: Build packages from local files or GitHub release assets and publish the verified result.
|
|
4
|
+
nav_order: 3
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Publishing a release
|
|
8
|
+
|
|
9
|
+
Once you have [built and tested your packages](getting-started.md), you can
|
|
10
|
+
attach them to a GitHub release. This guide covers both local inputs and
|
|
11
|
+
binaries that are already uploaded to a release.
|
|
12
|
+
|
|
13
|
+
## 1. Choose the release repository
|
|
14
|
+
|
|
15
|
+
Add your GitHub repository to `native-packages.yaml` before building:
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
release:
|
|
19
|
+
repository: your-name/hello
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This is where native-packages downloads release inputs and uploads packages.
|
|
23
|
+
It can be a separate repository from your application's source code.
|
|
24
|
+
|
|
25
|
+
## 2. Build your packages
|
|
26
|
+
|
|
27
|
+
### From local files
|
|
28
|
+
|
|
29
|
+
Use the same command as during development:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
native-packages build --version 1.2.3
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
This reads each target's `input.local`. You can build and test the packages
|
|
36
|
+
before creating a GitHub release.
|
|
37
|
+
|
|
38
|
+
### From an existing GitHub release
|
|
39
|
+
|
|
40
|
+
To package binaries your release workflow has already uploaded, declare each
|
|
41
|
+
target's release asset name:
|
|
42
|
+
|
|
43
|
+
```yaml
|
|
44
|
+
input:
|
|
45
|
+
kind: archive
|
|
46
|
+
local: dist/hello_@VERSION@_linux_amd64.tar.gz
|
|
47
|
+
release_asset: hello_@VERSION@_linux_amd64.tar.gz
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The local path remains useful for development. `release_asset` is the exact
|
|
51
|
+
filename attached to the GitHub release; `@VERSION@` becomes `1.2.3`.
|
|
52
|
+
|
|
53
|
+
Upload a `checksums.txt` alongside the input archives. It must contain SHA-256
|
|
54
|
+
hashes with the release filenames. For example, run this in the directory
|
|
55
|
+
containing the archive, then upload both files through your release workflow:
|
|
56
|
+
|
|
57
|
+
```sh
|
|
58
|
+
sha256sum hello_1.2.3_linux_amd64.tar.gz > checksums.txt
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
For several binary inputs, include all of them in the same checksum file.
|
|
62
|
+
If your workflow uses another filename, set `release.checksums` to that name.
|
|
63
|
+
|
|
64
|
+
Now build from the published release:
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
native-packages build --release v1.2.3
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The command downloads the inputs, verifies their published hashes, and builds
|
|
71
|
+
packages in `dist/packages/1.2.3/`. It skips `before_build` hooks. Every binary
|
|
72
|
+
release input needs a matching checksum.
|
|
73
|
+
|
|
74
|
+
DMG and Inno targets use prepared local directories. Download and unpack their
|
|
75
|
+
inputs in the native build job, then use `--version`. See
|
|
76
|
+
[macOS and Windows installers](native-recipes.md).
|
|
77
|
+
|
|
78
|
+
## 3. Upload the finished build
|
|
79
|
+
|
|
80
|
+
Install and authenticate the [GitHub CLI](https://cli.github.com/), and create
|
|
81
|
+
release `v1.2.3` in the configured repository using your normal release process.
|
|
82
|
+
The release must exist before you publish packages.
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
native-packages publish --from dist/packages/1.2.3 --to github
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Before uploading, native-packages verifies the recorded file hashes, the
|
|
89
|
+
configuration, and the required targets and formats. Keep the build directory
|
|
90
|
+
intact and use the same configuration you built with.
|
|
91
|
+
|
|
92
|
+
The upload includes the package files, a recipe archive if one was generated,
|
|
93
|
+
and `packaging-checksums.txt`. Your original `checksums.txt` is preserved.
|
|
94
|
+
Files with matching names on the release are replaced, so publish the tested
|
|
95
|
+
build you intend users to download.
|
|
96
|
+
|
|
97
|
+
## Publish selected targets
|
|
98
|
+
|
|
99
|
+
By default, publication requires every configured target and all its formats.
|
|
100
|
+
If one configuration includes targets released by separate jobs, name exactly
|
|
101
|
+
which complete targets this build contains:
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
native-packages build --release v1.2.3 \
|
|
105
|
+
--target linux-amd64 --output dist/linux-packages
|
|
106
|
+
native-packages publish --from dist/linux-packages --to github \
|
|
107
|
+
--target linux-amd64
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
All formats for `linux-amd64` must be present, and the directory must contain
|
|
111
|
+
no additional targets. A build made with `--format deb` cannot be published
|
|
112
|
+
if that target also declares RPM.
|
|
113
|
+
|
|
114
|
+
To combine builds from several jobs, follow
|
|
115
|
+
[Building across platforms](multi-platform.md). Builds with deferred recipes
|
|
116
|
+
must be finalized before publication.
|
|
117
|
+
|
|
118
|
+
## Update distribution recipes
|
|
119
|
+
|
|
120
|
+
For AUR, Homebrew, or another downstream repository, first configure its
|
|
121
|
+
[recipe templates and destination](distribution-recipes.md). Then stage and
|
|
122
|
+
review the generated update:
|
|
123
|
+
|
|
124
|
+
```sh
|
|
125
|
+
native-packages stage aur dist/packages/1.2.3/recipes
|
|
126
|
+
native-packages diff aur
|
|
127
|
+
native-packages publish aur
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Once this is part of your tested release process, you can publish configured
|
|
131
|
+
destinations together:
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
native-packages publish --from dist/packages/1.2.3 --to github,aur,homebrew
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
GitHub pull requests and GitLab merge requests require a reviewed description
|
|
138
|
+
with `--body-file`. Publish those destinations individually.
|
|
139
|
+
|
|
140
|
+
## Next steps
|
|
141
|
+
|
|
142
|
+
Set up [GitHub Actions](github-actions.md) to run packaging for each release.
|
|
143
|
+
For alpha and beta versions, read [Prereleases](prereleases.md).
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Release signatures and attestations
|
|
3
|
+
description: Share release signing machinery while keeping application keys, approval policy and build identity separate.
|
|
4
|
+
nav_order: 10
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Release signatures and attestations
|
|
8
|
+
|
|
9
|
+
Publisher signatures authorize release downloads. Build attestations identify
|
|
10
|
+
which repository, workflow and commit produced an artifact. Use both when your
|
|
11
|
+
application downloads its own updates. Neither proves the source code is safe.
|
|
12
|
+
These features are available in native-packages 0.7.0 and later. Use the
|
|
13
|
+
released gem for local commands and pin composite actions to the reviewed
|
|
14
|
+
commit behind the corresponding release tag.
|
|
15
|
+
|
|
16
|
+
## Keep the trust policy in the application
|
|
17
|
+
|
|
18
|
+
Each application owns its private key, committed public key, updater verification
|
|
19
|
+
and release approval rules. Do not share a key between unrelated applications,
|
|
20
|
+
store private keys in native-packages, or fetch an updater's trust root from the
|
|
21
|
+
same release it is checking. There is no unsigned fallback when signing is used.
|
|
22
|
+
|
|
23
|
+
Store the private PKCS#8 PEM key in a protected GitHub environment in the
|
|
24
|
+
application repository. Restrict that environment to release tags and require
|
|
25
|
+
maintainer approval. Keep an encrypted backup outside GitHub. Approval should
|
|
26
|
+
cover the exact commit, build results and artifacts, not just a version label.
|
|
27
|
+
Only the final signing job needs this secret. PR/build jobs do not.
|
|
28
|
+
|
|
29
|
+
This uses GitHub-hosted key custody, not an independent offline authority.
|
|
30
|
+
Maintain separate Apple notarization and Windows Authenticode credentials where
|
|
31
|
+
needed; a checksum signature does not replace either platform's signing system.
|
|
32
|
+
|
|
33
|
+
## Generate, sign and verify locally
|
|
34
|
+
|
|
35
|
+
Put only the finished release artifacts in a staging directory. Do not include
|
|
36
|
+
private keys, source checkout folders or intermediate build files. Keep the
|
|
37
|
+
public key outside this directory too.
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
native-packages release-checksums dist/release --output dist/release/checksums.txt
|
|
41
|
+
native-packages sign-checksums dist/release/checksums.txt --public-key assets/update-public-key.hex
|
|
42
|
+
native-packages verify-checksums dist/release/checksums.txt --public-key assets/update-public-key.hex
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Provide the private PEM through `NATIVE_PACKAGES_SIGNING_KEY` using your secret
|
|
46
|
+
manager. `--key-env VARIABLE` selects another environment variable, not its
|
|
47
|
+
value. Never put a private key on a command line or enable shell tracing.
|
|
48
|
+
Signing reads the key in-process, removes it from that process's environment,
|
|
49
|
+
and creates no private key file. It does not promise secure erasure of Ruby or
|
|
50
|
+
OpenSSL memory. Missing, malformed, wrong or non-Ed25519 keys fail closed.
|
|
51
|
+
|
|
52
|
+
The output is a deterministic, sorted SHA-256 manifest plus a raw 64-byte
|
|
53
|
+
Ed25519 signature over its exact bytes. The public-key file is 32 bytes encoded
|
|
54
|
+
as 64 hex digits. This is Ed25519, not Ed25519ph. Signing verifies the listed
|
|
55
|
+
files first; verification authenticates the manifest before reading its paths.
|
|
56
|
+
Metadata is limited to 1 MiB. Filenames must be flat ASCII names beginning with
|
|
57
|
+
a letter or digit, using letters, digits, dots, underscores, plus or minus.
|
|
58
|
+
Symlinks, directories, duplicate names (including case collisions), path
|
|
59
|
+
traversal and self-references are rejected. Existing outputs are not overwritten.
|
|
60
|
+
Upload artifacts, `checksums.txt` and `checksums.txt.sig` together.
|
|
61
|
+
|
|
62
|
+
These are explicit commands. Existing `build --release` and `publish` behavior
|
|
63
|
+
is unchanged: they do not silently gain signature verification or signing.
|
|
64
|
+
To verify a downloaded release, stage its files and explicitly run
|
|
65
|
+
`verify-checksums` with your locally trusted public key before using those files.
|
|
66
|
+
Package publication's separate `packaging-checksums.txt` is also unchanged.
|
|
67
|
+
|
|
68
|
+
## Attest in the application's build job
|
|
69
|
+
|
|
70
|
+
The examples use `v0.7.0` for readability. For release workflows, replace it
|
|
71
|
+
with that release's reviewed full commit SHA, not a mutable branch.
|
|
72
|
+
After producing the final package, before uploading it, add this step to the
|
|
73
|
+
job that built it:
|
|
74
|
+
|
|
75
|
+
```yaml
|
|
76
|
+
permissions:
|
|
77
|
+
contents: read
|
|
78
|
+
id-token: write
|
|
79
|
+
attestations: write
|
|
80
|
+
steps:
|
|
81
|
+
# Existing checkout, build, platform signing and packaging steps go here.
|
|
82
|
+
- uses: crmne/native-packages/.github/actions/attest@v0.7.0
|
|
83
|
+
with:
|
|
84
|
+
subject-path: dist/*.tar.gz
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`subject-path` accepts a glob or newline-separated list. The composite action
|
|
88
|
+
runs inside the calling job, preserving the application's build identity.
|
|
89
|
+
It uses GitHub's pinned `actions/attest` implementation and needs no publisher
|
|
90
|
+
key. It exposes `bundle-path` and `attestation-url` outputs. Each platform job
|
|
91
|
+
attests its own final artifacts. A packaging-only job can attest the package it
|
|
92
|
+
made, but that does not establish provenance for an earlier executable build.
|
|
93
|
+
|
|
94
|
+
Consumers can verify a file with:
|
|
95
|
+
|
|
96
|
+
```sh
|
|
97
|
+
gh attestation verify FILE --repo OWNER/APP
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
For tighter policy, also constrain `--signer-workflow` and `--source-digest`.
|
|
101
|
+
See [GitHub's artifact attestation documentation](https://docs.github.com/en/actions/how-tos/secure-your-work/use-artifact-attestations/use-artifact-attestations).
|
|
102
|
+
|
|
103
|
+
## Sign in the approved publication job
|
|
104
|
+
|
|
105
|
+
After every build passes, download the final artifacts into a flat directory in
|
|
106
|
+
a fresh signing job. Set its `environment: release-signing` in the app workflow
|
|
107
|
+
and install Ruby 3.2 or later with Ed25519-capable OpenSSL before the signing
|
|
108
|
+
step. Then call:
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
- uses: crmne/native-packages/.github/actions/sign-release@v0.7.0
|
|
112
|
+
with:
|
|
113
|
+
directory: dist/release
|
|
114
|
+
public-key: assets/update-public-key.hex
|
|
115
|
+
env:
|
|
116
|
+
NATIVE_PACKAGES_SIGNING_KEY: ${{ secrets.UPDATE_SIGNING_KEY }}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The action uses the CLI from its own pinned source commit. It neither installs
|
|
120
|
+
the gem nor runs project hooks, builds, tags, publishes or approves deployments.
|
|
121
|
+
The caller checks out its committed public key and owns the publication step.
|
|
122
|
+
For an existing secret variable name, set the action's `key-env` input.
|
|
123
|
+
The action requires a fresh directory without an old checksum/signature pair.
|
|
124
|
+
|
|
125
|
+
## Updater integration and key changes
|
|
126
|
+
|
|
127
|
+
Embed the public key in the app. Reject missing or invalid signatures before
|
|
128
|
+
accepting a checksum or downloading a package. Bind checksums to exact
|
|
129
|
+
version/platform filenames, reject downgrade/replay updates, and retain package
|
|
130
|
+
size checks, installation acknowledgement and rollback.
|
|
131
|
+
|
|
132
|
+
The initial signature-enforcing version needs a trusted installation. Old
|
|
133
|
+
hash-only clients are not protected until upgraded. Changing the key requires a
|
|
134
|
+
tested compatibility transition in each app; the single-signature format here
|
|
135
|
+
does not provide automatic key rotation. If a key is lost or compromised, stop
|
|
136
|
+
automatic publication, revoke the secret and use an independently verified
|
|
137
|
+
recovery installer. Do not replace an updater's trusted key from unsigned data.
|