chore(release): prepare V1.0.2
This commit is contained in:
120
.github/workflows/release.yml
vendored
120
.github/workflows/release.yml
vendored
@@ -54,9 +54,102 @@ jobs:
|
|||||||
path: bin/*-darwin-*
|
path: bin/*-darwin-*
|
||||||
retention-days: 1
|
retention-days: 1
|
||||||
|
|
||||||
|
native:
|
||||||
|
name: Build Native Release
|
||||||
|
runs-on: macos-latest
|
||||||
|
outputs:
|
||||||
|
packaging_mode: ${{ steps.mode.outputs.packaging_mode }}
|
||||||
|
prerelease: ${{ steps.mode.outputs.prerelease }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v4
|
||||||
|
|
||||||
|
- name: Derive native release version
|
||||||
|
run: |
|
||||||
|
echo "ATLAS_VERSION=${GITHUB_REF_NAME#V}" >> "$GITHUB_ENV"
|
||||||
|
echo "ATLAS_BUILD_NUMBER=${GITHUB_RUN_NUMBER}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
- name: Select native packaging mode
|
||||||
|
id: mode
|
||||||
|
env:
|
||||||
|
ATLAS_RELEASE_APP_CERT_P12_BASE64: ${{ secrets.ATLAS_RELEASE_APP_CERT_P12_BASE64 }}
|
||||||
|
ATLAS_RELEASE_APP_CERT_P12_PASSWORD: ${{ secrets.ATLAS_RELEASE_APP_CERT_P12_PASSWORD }}
|
||||||
|
ATLAS_RELEASE_INSTALLER_CERT_P12_BASE64: ${{ secrets.ATLAS_RELEASE_INSTALLER_CERT_P12_BASE64 }}
|
||||||
|
ATLAS_RELEASE_INSTALLER_CERT_P12_PASSWORD: ${{ secrets.ATLAS_RELEASE_INSTALLER_CERT_P12_PASSWORD }}
|
||||||
|
ATLAS_NOTARY_KEY_ID: ${{ secrets.ATLAS_NOTARY_KEY_ID }}
|
||||||
|
ATLAS_NOTARY_ISSUER_ID: ${{ secrets.ATLAS_NOTARY_ISSUER_ID }}
|
||||||
|
ATLAS_NOTARY_API_KEY_BASE64: ${{ secrets.ATLAS_NOTARY_API_KEY_BASE64 }}
|
||||||
|
run: |
|
||||||
|
required_vars=(
|
||||||
|
ATLAS_RELEASE_APP_CERT_P12_BASE64
|
||||||
|
ATLAS_RELEASE_APP_CERT_P12_PASSWORD
|
||||||
|
ATLAS_RELEASE_INSTALLER_CERT_P12_BASE64
|
||||||
|
ATLAS_RELEASE_INSTALLER_CERT_P12_PASSWORD
|
||||||
|
ATLAS_NOTARY_KEY_ID
|
||||||
|
ATLAS_NOTARY_API_KEY_BASE64
|
||||||
|
)
|
||||||
|
|
||||||
|
missing_vars=()
|
||||||
|
for name in "${required_vars[@]}"; do
|
||||||
|
if [[ -z "${!name:-}" ]]; then
|
||||||
|
missing_vars+=("$name")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ ${#missing_vars[@]} -eq 0 ]]; then
|
||||||
|
echo "packaging_mode=developer-id" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "prerelease=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "ATLAS_RELEASE_SIGNING_MODE=developer-id" >> "$GITHUB_ENV"
|
||||||
|
echo "Using Developer ID release packaging"
|
||||||
|
else
|
||||||
|
echo "packaging_mode=development" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "prerelease=true" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "ATLAS_RELEASE_SIGNING_MODE=development" >> "$GITHUB_ENV"
|
||||||
|
printf 'Falling back to development packaging; missing secrets: %s\n' "${missing_vars[*]}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Configure release signing
|
||||||
|
if: steps.mode.outputs.packaging_mode == 'developer-id'
|
||||||
|
env:
|
||||||
|
ATLAS_RELEASE_APP_CERT_P12_BASE64: ${{ secrets.ATLAS_RELEASE_APP_CERT_P12_BASE64 }}
|
||||||
|
ATLAS_RELEASE_APP_CERT_P12_PASSWORD: ${{ secrets.ATLAS_RELEASE_APP_CERT_P12_PASSWORD }}
|
||||||
|
ATLAS_RELEASE_INSTALLER_CERT_P12_BASE64: ${{ secrets.ATLAS_RELEASE_INSTALLER_CERT_P12_BASE64 }}
|
||||||
|
ATLAS_RELEASE_INSTALLER_CERT_P12_PASSWORD: ${{ secrets.ATLAS_RELEASE_INSTALLER_CERT_P12_PASSWORD }}
|
||||||
|
ATLAS_NOTARY_KEY_ID: ${{ secrets.ATLAS_NOTARY_KEY_ID }}
|
||||||
|
ATLAS_NOTARY_ISSUER_ID: ${{ secrets.ATLAS_NOTARY_ISSUER_ID }}
|
||||||
|
ATLAS_NOTARY_API_KEY_BASE64: ${{ secrets.ATLAS_NOTARY_API_KEY_BASE64 }}
|
||||||
|
run: ./scripts/atlas/setup-release-signing-ci.sh
|
||||||
|
|
||||||
|
- name: Provision local development signing identity
|
||||||
|
if: steps.mode.outputs.packaging_mode == 'development'
|
||||||
|
run: ./scripts/atlas/ensure-local-signing-identity.sh
|
||||||
|
|
||||||
|
- name: Validate signing prerequisites
|
||||||
|
if: steps.mode.outputs.packaging_mode == 'developer-id'
|
||||||
|
run: ./scripts/atlas/signing-preflight.sh
|
||||||
|
|
||||||
|
- name: Build and package Atlas native app
|
||||||
|
run: ./scripts/atlas/package-native.sh
|
||||||
|
|
||||||
|
- name: Verify DMG can install to the user Applications folder
|
||||||
|
run: KEEP_INSTALLED_APP=1 ./scripts/atlas/verify-dmg-install.sh
|
||||||
|
|
||||||
|
- name: Upload native release artifacts
|
||||||
|
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
|
||||||
|
with:
|
||||||
|
name: atlas-native-release
|
||||||
|
path: |
|
||||||
|
dist/native/Atlas-for-Mac.zip
|
||||||
|
dist/native/Atlas-for-Mac.dmg
|
||||||
|
dist/native/Atlas-for-Mac.pkg
|
||||||
|
dist/native/Atlas-for-Mac.sha256
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
release:
|
release:
|
||||||
name: Publish Release
|
name: Publish Release
|
||||||
needs: build
|
needs:
|
||||||
|
- build
|
||||||
|
- native
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -70,6 +163,24 @@ jobs:
|
|||||||
pattern: binaries-*
|
pattern: binaries-*
|
||||||
merge-multiple: true
|
merge-multiple: true
|
||||||
|
|
||||||
|
- name: Download native release artifacts
|
||||||
|
uses: actions/download-artifact@70fc10c6e5e1ce46ad2ea6f2b72d43f7d47b13c3 # v8.0.0
|
||||||
|
with:
|
||||||
|
name: atlas-native-release
|
||||||
|
path: bin
|
||||||
|
|
||||||
|
- name: Generate release body
|
||||||
|
run: |
|
||||||
|
if [[ "${{ needs.native.outputs.packaging_mode }}" == "development" ]]; then
|
||||||
|
{
|
||||||
|
echo "Native macOS assets in this tag were packaged in development mode because Developer ID release-signing credentials were not configured for this run."
|
||||||
|
echo
|
||||||
|
echo "These \`.zip\`, \`.dmg\`, and \`.pkg\` files are intended for internal testing or developer use. macOS Gatekeeper may require \`Open Anyway\` or a right-click \`Open\` flow before launch."
|
||||||
|
} > RELEASE_BODY.md
|
||||||
|
else
|
||||||
|
echo "Native macOS assets in this tag were packaged in CI using Developer ID signing and notarization, then uploaded alongside the existing command-line release artifacts." > RELEASE_BODY.md
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Display structure of downloaded files
|
- name: Display structure of downloaded files
|
||||||
run: ls -R bin/
|
run: ls -R bin/
|
||||||
|
|
||||||
@@ -91,6 +202,10 @@ jobs:
|
|||||||
bin/analyze-darwin-*
|
bin/analyze-darwin-*
|
||||||
bin/status-darwin-*
|
bin/status-darwin-*
|
||||||
bin/binaries-darwin-*.tar.gz
|
bin/binaries-darwin-*.tar.gz
|
||||||
|
bin/Atlas-for-Mac.zip
|
||||||
|
bin/Atlas-for-Mac.dmg
|
||||||
|
bin/Atlas-for-Mac.pkg
|
||||||
|
bin/Atlas-for-Mac.sha256
|
||||||
bin/SHA256SUMS
|
bin/SHA256SUMS
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
@@ -99,6 +214,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
name: ${{ github.ref_name }}
|
name: ${{ github.ref_name }}
|
||||||
files: bin/*
|
files: bin/*
|
||||||
|
body_path: RELEASE_BODY.md
|
||||||
generate_release_notes: false
|
generate_release_notes: false
|
||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: ${{ needs.native.outputs.prerelease == 'true' }}
|
||||||
|
|||||||
@@ -100,11 +100,11 @@ final class AtlasAppModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var appVersion: String {
|
var appVersion: String {
|
||||||
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.1"
|
Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "1.0.2"
|
||||||
}
|
}
|
||||||
|
|
||||||
var appBuild: String {
|
var appBuild: String {
|
||||||
Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "2"
|
Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "3"
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkForUpdate() async {
|
func checkForUpdate() async {
|
||||||
|
|||||||
@@ -458,7 +458,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
AD_HOC_CODE_SIGNING_ALLOWED = YES;
|
AD_HOC_CODE_SIGNING_ALLOWED = YES;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 2;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleShortVersionString = "$(MARKETING_VERSION)";
|
INFOPLIST_KEY_CFBundleShortVersionString = "$(MARKETING_VERSION)";
|
||||||
INFOPLIST_KEY_CFBundleVersion = "$(CURRENT_PROJECT_VERSION)";
|
INFOPLIST_KEY_CFBundleVersion = "$(CURRENT_PROJECT_VERSION)";
|
||||||
@@ -467,7 +467,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1.0.1;
|
MARKETING_VERSION = 1.0.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atlasformac.app.worker;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atlasformac.app.worker;
|
||||||
PRODUCT_NAME = AtlasWorkerXPC;
|
PRODUCT_NAME = AtlasWorkerXPC;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -535,7 +535,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
AD_HOC_CODE_SIGNING_ALLOWED = YES;
|
AD_HOC_CODE_SIGNING_ALLOWED = YES;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 2;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleShortVersionString = "$(MARKETING_VERSION)";
|
INFOPLIST_KEY_CFBundleShortVersionString = "$(MARKETING_VERSION)";
|
||||||
INFOPLIST_KEY_CFBundleVersion = "$(CURRENT_PROJECT_VERSION)";
|
INFOPLIST_KEY_CFBundleVersion = "$(CURRENT_PROJECT_VERSION)";
|
||||||
@@ -544,7 +544,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1.0.1;
|
MARKETING_VERSION = 1.0.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atlasformac.app.worker;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atlasformac.app.worker;
|
||||||
PRODUCT_NAME = AtlasWorkerXPC;
|
PRODUCT_NAME = AtlasWorkerXPC;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -557,7 +557,7 @@
|
|||||||
AD_HOC_CODE_SIGNING_ALLOWED = YES;
|
AD_HOC_CODE_SIGNING_ALLOWED = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 2;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Atlas for Mac";
|
INFOPLIST_KEY_CFBundleDisplayName = "Atlas for Mac";
|
||||||
INFOPLIST_KEY_CFBundleShortVersionString = "$(MARKETING_VERSION)";
|
INFOPLIST_KEY_CFBundleShortVersionString = "$(MARKETING_VERSION)";
|
||||||
@@ -569,7 +569,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1.0.1;
|
MARKETING_VERSION = 1.0.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atlasformac.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atlasformac.app;
|
||||||
PRODUCT_MODULE_NAME = AtlasApp;
|
PRODUCT_MODULE_NAME = AtlasApp;
|
||||||
PRODUCT_NAME = "Atlas for Mac";
|
PRODUCT_NAME = "Atlas for Mac";
|
||||||
@@ -665,7 +665,7 @@
|
|||||||
AD_HOC_CODE_SIGNING_ALLOWED = YES;
|
AD_HOC_CODE_SIGNING_ALLOWED = YES;
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 2;
|
CURRENT_PROJECT_VERSION = 3;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Atlas for Mac";
|
INFOPLIST_KEY_CFBundleDisplayName = "Atlas for Mac";
|
||||||
INFOPLIST_KEY_CFBundleShortVersionString = "$(MARKETING_VERSION)";
|
INFOPLIST_KEY_CFBundleShortVersionString = "$(MARKETING_VERSION)";
|
||||||
@@ -677,7 +677,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
MACOSX_DEPLOYMENT_TARGET = 14.0;
|
||||||
MARKETING_VERSION = 1.0.1;
|
MARKETING_VERSION = 1.0.2;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.atlasformac.app;
|
PRODUCT_BUNDLE_IDENTIFIER = com.atlasformac.app;
|
||||||
PRODUCT_MODULE_NAME = AtlasApp;
|
PRODUCT_MODULE_NAME = AtlasApp;
|
||||||
PRODUCT_NAME = "Atlas for Mac";
|
PRODUCT_NAME = "Atlas for Mac";
|
||||||
|
|||||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [1.0.2] - 2026-03-14
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Tag-driven GitHub Releases now publish Atlas native `.zip`, `.dmg`, and `.pkg` assets in addition to the legacy command-line binaries.
|
||||||
|
- Added `scripts/atlas/prepare-release.sh` to align app version, build number, and changelog scaffolding before tagging a release.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Release automation now falls back to a development-signed prerelease path when `Developer ID` signing credentials are unavailable, instead of blocking native packaging entirely.
|
||||||
|
- README installation guidance now distinguishes signed public releases from development prereleases and recommends local stable signing for developer packaging.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- `package-native.sh` and `signing-preflight.sh` now support `notarytool` profiles stored in a non-default keychain, which unblocks CI-based notarization.
|
||||||
|
|
||||||
## [1.0.1] - 2026-03-13
|
## [1.0.1] - 2026-03-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -72,6 +72,8 @@
|
|||||||
- Distribution target: `Developer ID + Hardened Runtime + Notarization`
|
- Distribution target: `Developer ID + Hardened Runtime + Notarization`
|
||||||
- Initial release target: direct distribution, not Mac App Store
|
- Initial release target: direct distribution, not Mac App Store
|
||||||
- Native packaging currently uses `xcodegen + xcodebuild`, embeds the helper into `Contents/Helpers/`, and emits `.zip`, `.dmg`, and `.pkg` distribution artifacts.
|
- Native packaging currently uses `xcodegen + xcodebuild`, embeds the helper into `Contents/Helpers/`, and emits `.zip`, `.dmg`, and `.pkg` distribution artifacts.
|
||||||
|
- Tagged GitHub Releases reuse the same native packaging scripts in CI and publish `.zip`, `.dmg`, `.pkg`, and checksum assets.
|
||||||
|
- When release signing credentials are configured, CI signs and notarizes those assets; otherwise it falls back to a local development signing identity and marks the GitHub Release as a prerelease.
|
||||||
- Local internal packaging now prefers a stable non-ad-hoc app signature when a usable identity is available, so macOS TCC decisions can survive rebuilds more reliably during development.
|
- Local internal packaging now prefers a stable non-ad-hoc app signature when a usable identity is available, so macOS TCC decisions can survive rebuilds more reliably during development.
|
||||||
- If Apple release certificates are unavailable, Atlas can fall back to a repo-managed local signing keychain for stable app-bundle identity; public release artifacts still require `Developer ID`.
|
- If Apple release certificates are unavailable, Atlas can fall back to a repo-managed local signing keychain for stable app-bundle identity; public release artifacts still require `Developer ID`.
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,8 @@
|
|||||||
|
|
||||||
- Destructive helper actions use a structured executable boundary with path validation
|
- Destructive helper actions use a structured executable boundary with path validation
|
||||||
- Native MVP packaging uses `xcodegen + xcodebuild`, then embeds the helper into the app bundle
|
- Native MVP packaging uses `xcodegen + xcodebuild`, then embeds the helper into the app bundle
|
||||||
|
- Tagged GitHub Releases should publish the native `.zip`, `.dmg`, and `.pkg` assets from CI using the same packaging scripts as local release builds
|
||||||
|
- If CI lacks `Developer ID` release credentials, tagged native assets may still be published as development-signed prereleases instead of blocking the packaging path entirely
|
||||||
- Signing and notarization remain optional release-time steps driven by credentials
|
- Signing and notarization remain optional release-time steps driven by credentials
|
||||||
- Internal packaging should prefer a stable local app-signing identity over ad hoc signing whenever possible so macOS permission state does not drift across rebuilds
|
- Internal packaging should prefer a stable local app-signing identity over ad hoc signing whenever possible so macOS permission state does not drift across rebuilds
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ Turn Atlas for Mac from an installable local build into a publicly distributable
|
|||||||
- `ATLAS_CODESIGN_KEYCHAIN`
|
- `ATLAS_CODESIGN_KEYCHAIN`
|
||||||
- `ATLAS_INSTALLER_SIGN_IDENTITY`
|
- `ATLAS_INSTALLER_SIGN_IDENTITY`
|
||||||
- `ATLAS_NOTARY_PROFILE`
|
- `ATLAS_NOTARY_PROFILE`
|
||||||
|
- `ATLAS_NOTARY_KEYCHAIN` (optional; required when the notary profile lives in a non-default keychain such as CI)
|
||||||
|
|
||||||
## Stable Local Signing
|
## Stable Local Signing
|
||||||
|
|
||||||
@@ -43,6 +44,28 @@ Run:
|
|||||||
|
|
||||||
If preflight passes, the current machine is ready for signed packaging.
|
If preflight passes, the current machine is ready for signed packaging.
|
||||||
|
|
||||||
|
## Version Prep
|
||||||
|
|
||||||
|
Before pushing a release tag, align the app version, build number, and changelog skeleton:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/atlas/prepare-release.sh 1.0.2
|
||||||
|
```
|
||||||
|
|
||||||
|
Optional arguments:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/atlas/prepare-release.sh 1.0.2 3 2026-03-14
|
||||||
|
```
|
||||||
|
|
||||||
|
This updates:
|
||||||
|
|
||||||
|
- `project.yml`
|
||||||
|
- `Apps/AtlasApp/Sources/AtlasApp/AtlasAppModel.swift`
|
||||||
|
- `CHANGELOG.md`
|
||||||
|
|
||||||
|
The script increments `CURRENT_PROJECT_VERSION` automatically when you omit the build number. Review the new changelog section before creating the `V1.0.2` tag.
|
||||||
|
|
||||||
## Signed Packaging
|
## Signed Packaging
|
||||||
|
|
||||||
Run:
|
Run:
|
||||||
@@ -56,6 +79,12 @@ ATLAS_NOTARY_PROFILE="<profile-name>" \
|
|||||||
|
|
||||||
This signs the app bundle, emits `.zip`, `.dmg`, and `.pkg`, submits artifacts for notarization, and staples results when credentials are available.
|
This signs the app bundle, emits `.zip`, `.dmg`, and `.pkg`, submits artifacts for notarization, and staples results when credentials are available.
|
||||||
|
|
||||||
|
If the notary profile is stored in a non-default keychain, also set:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ATLAS_NOTARY_KEYCHAIN="/path/to/release.keychain-db"
|
||||||
|
```
|
||||||
|
|
||||||
## Install Verification
|
## Install Verification
|
||||||
|
|
||||||
After packaging, validate the DMG installation path with:
|
After packaging, validate the DMG installation path with:
|
||||||
@@ -64,7 +93,52 @@ After packaging, validate the DMG installation path with:
|
|||||||
KEEP_INSTALLED_APP=1 ./scripts/atlas/verify-dmg-install.sh
|
KEEP_INSTALLED_APP=1 ./scripts/atlas/verify-dmg-install.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## GitHub Tag Release Automation
|
||||||
|
|
||||||
|
Tagged pushes matching `V*` now reuse the same packaging flow in CI and attach native release assets to the GitHub Release created by `.github/workflows/release.yml`.
|
||||||
|
|
||||||
|
Required GitHub Actions secrets:
|
||||||
|
|
||||||
|
- `ATLAS_RELEASE_APP_CERT_P12_BASE64`
|
||||||
|
- `ATLAS_RELEASE_APP_CERT_P12_PASSWORD`
|
||||||
|
- `ATLAS_RELEASE_INSTALLER_CERT_P12_BASE64`
|
||||||
|
- `ATLAS_RELEASE_INSTALLER_CERT_P12_PASSWORD`
|
||||||
|
- `ATLAS_NOTARY_KEY_ID`
|
||||||
|
- `ATLAS_NOTARY_ISSUER_ID` for Team API keys; omit only if you intentionally use an Individual API key
|
||||||
|
- `ATLAS_NOTARY_API_KEY_BASE64`
|
||||||
|
|
||||||
|
If those secrets are present, the workflow bootstraps a temporary keychain with `./scripts/atlas/setup-release-signing-ci.sh`, stores a `notarytool` profile there, derives `ATLAS_VERSION` from the pushed tag name, then runs `./scripts/atlas/package-native.sh`.
|
||||||
|
|
||||||
|
If those secrets are missing, the workflow automatically falls back to:
|
||||||
|
|
||||||
|
- `./scripts/atlas/ensure-local-signing-identity.sh`
|
||||||
|
- local development signing for the app bundle
|
||||||
|
- unsigned installer packaging if no installer identity exists
|
||||||
|
- no notarization
|
||||||
|
- GitHub Release marked as `prerelease`
|
||||||
|
|
||||||
|
Release flow:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git tag -a V1.0.2 -m "Release V1.0.2"
|
||||||
|
git push origin V1.0.2
|
||||||
|
```
|
||||||
|
|
||||||
|
That tag creates one GitHub Release containing:
|
||||||
|
|
||||||
|
- legacy Go binaries and Homebrew tarballs from the existing release pipeline
|
||||||
|
- `Atlas-for-Mac.zip`
|
||||||
|
- `Atlas-for-Mac.dmg`
|
||||||
|
- `Atlas-for-Mac.pkg`
|
||||||
|
- native and aggregate SHA-256 checksum files
|
||||||
|
|
||||||
|
Packaging mode by credential state:
|
||||||
|
|
||||||
|
- `Developer ID secrets present` -> signed and notarized native assets, normal GitHub Release
|
||||||
|
- `Developer ID secrets missing` -> development-signed native assets, GitHub `prerelease`
|
||||||
|
|
||||||
## Current Repo State
|
## Current Repo State
|
||||||
|
|
||||||
- Internal packaging can now use a stable local app-signing identity instead of ad hoc signing.
|
- Internal packaging can now use a stable local app-signing identity instead of ad hoc signing.
|
||||||
- Signed/notarized release artifacts remain blocked only by missing Apple release credentials on this machine.
|
- Signed/notarized release artifacts remain blocked only by missing Apple release credentials on this machine.
|
||||||
|
- Tagged GitHub Releases can still publish development-mode native assets without those credentials.
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ Download the latest release from the [Releases](https://github.com/CSZHK/CleanMy
|
|||||||
- **`.zip`** — Extract and move Atlas.app to your Applications folder.
|
- **`.zip`** — Extract and move Atlas.app to your Applications folder.
|
||||||
- **`.pkg`** — Run the installer package for guided installation.
|
- **`.pkg`** — Run the installer package for guided installation.
|
||||||
|
|
||||||
|
Prefer the latest non-prerelease release if you want the normal public install path. GitHub prereleases may contain development-signed builds intended for testing; those builds can require `Open Anyway` or a right-click `Open` flow before launch.
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- macOS 14.0 (Sonoma) or later
|
- macOS 14.0 (Sonoma) or later
|
||||||
@@ -51,7 +53,7 @@ xcodegen generate
|
|||||||
open Atlas.xcodeproj
|
open Atlas.xcodeproj
|
||||||
```
|
```
|
||||||
|
|
||||||
> **Note**: The app is currently unsigned. On first launch, you may need to right-click and select "Open" to bypass Gatekeeper, or go to System Settings > Privacy & Security to allow it.
|
> **Note**: Atlas release assets can be either `Developer ID signed + notarized` or `development prerelease` builds, depending on the release. If you install a prerelease or a local build, macOS may require `Open Anyway` or a right-click `Open` flow before launch.
|
||||||
|
|
||||||
## MVP Modules
|
## MVP Modules
|
||||||
|
|
||||||
@@ -115,9 +117,12 @@ open Atlas.xcodeproj
|
|||||||
### Package `.zip`, `.dmg`, and `.pkg` artifacts
|
### Package `.zip`, `.dmg`, and `.pkg` artifacts
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
./scripts/atlas/ensure-local-signing-identity.sh
|
||||||
./scripts/atlas/package-native.sh
|
./scripts/atlas/package-native.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The local signing step is recommended on machines that do not have Apple release certificates. It gives local and prerelease builds a stable development signature instead of falling back to ad hoc packaging.
|
||||||
|
|
||||||
### Run focused tests
|
### Run focused tests
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ Atlas for Mac 是一个独立的开源项目,与 Apple、Mole 上游作者或
|
|||||||
- **`.zip`** - 解压后将 Atlas.app 移动到 Applications 文件夹。
|
- **`.zip`** - 解压后将 Atlas.app 移动到 Applications 文件夹。
|
||||||
- **`.pkg`** - 运行安装包,按向导完成安装。
|
- **`.pkg`** - 运行安装包,按向导完成安装。
|
||||||
|
|
||||||
|
如果你想要正常的公开安装路径,优先下载最新的非 `prerelease` 版本。GitHub 上的 `prerelease` 版本可能包含用于测试的开发签名构建,这类构建在首次启动时可能需要 `仍要打开` 或右键 `打开`。
|
||||||
|
|
||||||
### 系统要求
|
### 系统要求
|
||||||
|
|
||||||
- macOS 14.0(Sonoma)或更高版本
|
- macOS 14.0(Sonoma)或更高版本
|
||||||
@@ -51,7 +53,7 @@ xcodegen generate
|
|||||||
open Atlas.xcodeproj
|
open Atlas.xcodeproj
|
||||||
```
|
```
|
||||||
|
|
||||||
> **说明**:应用当前尚未签名。首次启动时,你可能需要右键点击应用并选择“打开”以绕过 Gatekeeper,或前往“系统设置 > 隐私与安全性”手动允许启动。
|
> **说明**:Atlas 的发布包可能是 `Developer ID 签名 + 公证` 的正式构建,也可能是开发预发布构建,具体取决于该次发布。如果你安装的是预发布版本或本地构建版本,macOS 在首次启动时可能要求你使用 `仍要打开` 或右键 `打开`。
|
||||||
|
|
||||||
## MVP 模块
|
## MVP 模块
|
||||||
|
|
||||||
@@ -115,9 +117,12 @@ open Atlas.xcodeproj
|
|||||||
### 打包 `.zip`、`.dmg` 和 `.pkg` 产物
|
### 打包 `.zip`、`.dmg` 和 `.pkg` 产物
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
./scripts/atlas/ensure-local-signing-identity.sh
|
||||||
./scripts/atlas/package-native.sh
|
./scripts/atlas/package-native.sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
对于没有 Apple 发布证书的机器,建议先执行本地签名初始化。这样本地包和预发布开发包会带有稳定的开发签名,而不是退回到 ad hoc 打包。
|
||||||
|
|
||||||
### 运行聚焦测试
|
### 运行聚焦测试
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ targets:
|
|||||||
base:
|
base:
|
||||||
PRODUCT_BUNDLE_IDENTIFIER: com.atlasformac.app.worker
|
PRODUCT_BUNDLE_IDENTIFIER: com.atlasformac.app.worker
|
||||||
PRODUCT_NAME: AtlasWorkerXPC
|
PRODUCT_NAME: AtlasWorkerXPC
|
||||||
MARKETING_VERSION: "1.0.1"
|
MARKETING_VERSION: "1.0.2"
|
||||||
CURRENT_PROJECT_VERSION: 2
|
CURRENT_PROJECT_VERSION: 3
|
||||||
GENERATE_INFOPLIST_FILE: YES
|
GENERATE_INFOPLIST_FILE: YES
|
||||||
INFOPLIST_KEY_CFBundleShortVersionString: $(MARKETING_VERSION)
|
INFOPLIST_KEY_CFBundleShortVersionString: $(MARKETING_VERSION)
|
||||||
INFOPLIST_KEY_CFBundleVersion: $(CURRENT_PROJECT_VERSION)
|
INFOPLIST_KEY_CFBundleVersion: $(CURRENT_PROJECT_VERSION)
|
||||||
@@ -64,8 +64,8 @@ targets:
|
|||||||
PRODUCT_BUNDLE_IDENTIFIER: com.atlasformac.app
|
PRODUCT_BUNDLE_IDENTIFIER: com.atlasformac.app
|
||||||
PRODUCT_NAME: Atlas for Mac
|
PRODUCT_NAME: Atlas for Mac
|
||||||
PRODUCT_MODULE_NAME: AtlasApp
|
PRODUCT_MODULE_NAME: AtlasApp
|
||||||
MARKETING_VERSION: "1.0.1"
|
MARKETING_VERSION: "1.0.2"
|
||||||
CURRENT_PROJECT_VERSION: 2
|
CURRENT_PROJECT_VERSION: 3
|
||||||
GENERATE_INFOPLIST_FILE: YES
|
GENERATE_INFOPLIST_FILE: YES
|
||||||
INFOPLIST_KEY_CFBundleShortVersionString: $(MARKETING_VERSION)
|
INFOPLIST_KEY_CFBundleShortVersionString: $(MARKETING_VERSION)
|
||||||
INFOPLIST_KEY_CFBundleVersion: $(CURRENT_PROJECT_VERSION)
|
INFOPLIST_KEY_CFBundleVersion: $(CURRENT_PROJECT_VERSION)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ APP_SIGNING_KEYCHAIN="$(atlas_resolve_app_signing_keychain "$APP_SIGN_IDENTITY")
|
|||||||
APP_SIGNING_MODE="$(atlas_signing_mode_for_identity "$APP_SIGN_IDENTITY")"
|
APP_SIGNING_MODE="$(atlas_signing_mode_for_identity "$APP_SIGN_IDENTITY")"
|
||||||
INSTALLER_SIGN_IDENTITY="$(atlas_resolve_installer_signing_identity)"
|
INSTALLER_SIGN_IDENTITY="$(atlas_resolve_installer_signing_identity)"
|
||||||
NOTARY_PROFILE="${ATLAS_NOTARY_PROFILE:-}"
|
NOTARY_PROFILE="${ATLAS_NOTARY_PROFILE:-}"
|
||||||
|
NOTARY_KEYCHAIN="${ATLAS_NOTARY_KEYCHAIN:-}"
|
||||||
|
|
||||||
mkdir -p "$DIST_DIR"
|
mkdir -p "$DIST_DIR"
|
||||||
|
|
||||||
@@ -122,10 +123,15 @@ echo "Installer package: $PKG_PATH"
|
|||||||
echo "Checksums: $SHA_PATH"
|
echo "Checksums: $SHA_PATH"
|
||||||
|
|
||||||
if [[ -n "$NOTARY_PROFILE" && "$APP_SIGNING_MODE" == "developer-id" && -n "$INSTALLER_SIGN_IDENTITY" ]]; then
|
if [[ -n "$NOTARY_PROFILE" && "$APP_SIGNING_MODE" == "developer-id" && -n "$INSTALLER_SIGN_IDENTITY" ]]; then
|
||||||
xcrun notarytool submit "$PKG_PATH" --keychain-profile "$NOTARY_PROFILE" --wait
|
notarytool_args=(--keychain-profile "$NOTARY_PROFILE")
|
||||||
|
if [[ -n "$NOTARY_KEYCHAIN" ]]; then
|
||||||
|
notarytool_args+=(--keychain "$NOTARY_KEYCHAIN")
|
||||||
|
fi
|
||||||
|
|
||||||
|
xcrun notarytool submit "$PKG_PATH" "${notarytool_args[@]}" --wait
|
||||||
xcrun stapler staple "$PKG_PATH"
|
xcrun stapler staple "$PKG_PATH"
|
||||||
xcrun notarytool submit "$DMG_PATH" --keychain-profile "$NOTARY_PROFILE" --wait
|
xcrun notarytool submit "$DMG_PATH" "${notarytool_args[@]}" --wait
|
||||||
xcrun notarytool submit "$ZIP_PATH" --keychain-profile "$NOTARY_PROFILE" --wait
|
xcrun notarytool submit "$ZIP_PATH" "${notarytool_args[@]}" --wait
|
||||||
xcrun stapler staple "$PACKAGED_APP_PATH"
|
xcrun stapler staple "$PACKAGED_APP_PATH"
|
||||||
/usr/bin/ditto -c -k --sequesterRsrc --keepParent "$PACKAGED_APP_PATH" "$ZIP_PATH"
|
/usr/bin/ditto -c -k --sequesterRsrc --keepParent "$PACKAGED_APP_PATH" "$ZIP_PATH"
|
||||||
(
|
(
|
||||||
|
|||||||
105
scripts/atlas/prepare-release.sh
Executable file
105
scripts/atlas/prepare-release.sh
Executable file
@@ -0,0 +1,105 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ROOT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
|
||||||
|
PROJECT_FILE="$ROOT_DIR/project.yml"
|
||||||
|
APP_MODEL_FILE="$ROOT_DIR/Apps/AtlasApp/Sources/AtlasApp/AtlasAppModel.swift"
|
||||||
|
CHANGELOG_FILE="$ROOT_DIR/CHANGELOG.md"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'EOF'
|
||||||
|
Usage:
|
||||||
|
./scripts/atlas/prepare-release.sh <version> [build-number] [release-date]
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
./scripts/atlas/prepare-release.sh 1.0.2
|
||||||
|
./scripts/atlas/prepare-release.sh 1.0.2 3
|
||||||
|
./scripts/atlas/prepare-release.sh 1.0.2 3 2026-03-14
|
||||||
|
|
||||||
|
Behavior:
|
||||||
|
- updates MARKETING_VERSION and CURRENT_PROJECT_VERSION in project.yml
|
||||||
|
- updates AtlasApp fallback version/build strings
|
||||||
|
- inserts a changelog section for the requested version if it does not already exist
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ $# -lt 1 || $# -gt 3 ]]; then
|
||||||
|
usage >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION="$1"
|
||||||
|
BUILD_NUMBER="${2:-}"
|
||||||
|
RELEASE_DATE="${3:-$(date +%F)}"
|
||||||
|
|
||||||
|
if [[ ! "$VERSION" =~ ^[0-9]+(\.[0-9]+){1,2}([.-][0-9A-Za-z.-]+)?$ ]]; then
|
||||||
|
echo "Invalid version: $VERSION" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$BUILD_NUMBER" && ! "$BUILD_NUMBER" =~ ^[0-9]+$ ]]; then
|
||||||
|
echo "Build number must be numeric: $BUILD_NUMBER" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! "$RELEASE_DATE" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||||
|
echo "Release date must use YYYY-MM-DD: $RELEASE_DATE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$PROJECT_FILE" || ! -f "$APP_MODEL_FILE" || ! -f "$CHANGELOG_FILE" ]]; then
|
||||||
|
echo "Expected release files are missing." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$BUILD_NUMBER" ]]; then
|
||||||
|
current_build="$(
|
||||||
|
sed -n 's/.*CURRENT_PROJECT_VERSION: \([0-9][0-9]*\).*/\1/p' "$PROJECT_FILE" | head -1
|
||||||
|
)"
|
||||||
|
if [[ -z "$current_build" ]]; then
|
||||||
|
echo "Could not determine current build number from project.yml" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
BUILD_NUMBER="$((current_build + 1))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
current_version="$(
|
||||||
|
sed -n 's/.*MARKETING_VERSION: "\(.*\)"/\1/p' "$PROJECT_FILE" | head -1
|
||||||
|
)"
|
||||||
|
|
||||||
|
perl -0pi -e 's/MARKETING_VERSION: "[^"]+"/MARKETING_VERSION: "'"$VERSION"'"/g' "$PROJECT_FILE"
|
||||||
|
perl -0pi -e 's/CURRENT_PROJECT_VERSION: \d+/CURRENT_PROJECT_VERSION: '"$BUILD_NUMBER"'/g' "$PROJECT_FILE"
|
||||||
|
perl -0pi -e 's/(CFBundleShortVersionString"\] as\? String \?\? )"[^"]+"/${1}"'"$VERSION"'"/' "$APP_MODEL_FILE"
|
||||||
|
perl -0pi -e 's/(CFBundleVersion"\] as\? String \?\? )"[^"]+"/${1}"'"$BUILD_NUMBER"'"/' "$APP_MODEL_FILE"
|
||||||
|
|
||||||
|
if ! grep -Fq "## [$VERSION] - $RELEASE_DATE" "$CHANGELOG_FILE"; then
|
||||||
|
tmpfile="$(mktemp "${TMPDIR:-/tmp}/atlas-changelog.XXXXXX")"
|
||||||
|
awk -v version="$VERSION" -v date="$RELEASE_DATE" '
|
||||||
|
BEGIN { inserted = 0 }
|
||||||
|
{
|
||||||
|
print
|
||||||
|
if (!inserted && $0 == "## [Unreleased]") {
|
||||||
|
print ""
|
||||||
|
print "## [" version "] - " date
|
||||||
|
print ""
|
||||||
|
print "### Added"
|
||||||
|
print ""
|
||||||
|
print "### Changed"
|
||||||
|
print ""
|
||||||
|
print "### Fixed"
|
||||||
|
print ""
|
||||||
|
inserted = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
' "$CHANGELOG_FILE" > "$tmpfile"
|
||||||
|
mv "$tmpfile" "$CHANGELOG_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'Prepared Atlas release files\n'
|
||||||
|
printf 'Previous version: %s\n' "${current_version:-UNKNOWN}"
|
||||||
|
printf 'New version: %s\n' "$VERSION"
|
||||||
|
printf 'Build number: %s\n' "$BUILD_NUMBER"
|
||||||
|
printf 'Release date: %s\n' "$RELEASE_DATE"
|
||||||
|
printf 'Updated: %s\n' "$PROJECT_FILE"
|
||||||
|
printf 'Updated: %s\n' "$APP_MODEL_FILE"
|
||||||
|
printf 'Updated: %s\n' "$CHANGELOG_FILE"
|
||||||
151
scripts/atlas/setup-release-signing-ci.sh
Executable file
151
scripts/atlas/setup-release-signing-ci.sh
Executable file
@@ -0,0 +1,151 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
require_env() {
|
||||||
|
local name="$1"
|
||||||
|
if [[ -z "${!name:-}" ]]; then
|
||||||
|
echo "Missing required environment variable: $name" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
write_env() {
|
||||||
|
local name="$1"
|
||||||
|
local value="$2"
|
||||||
|
|
||||||
|
if [[ -n "${GITHUB_ENV:-}" ]]; then
|
||||||
|
printf '%s=%s\n' "$name" "$value" >> "$GITHUB_ENV"
|
||||||
|
else
|
||||||
|
printf 'export %s=%q\n' "$name" "$value"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
append_keychain_search_list() {
|
||||||
|
local keychain_path="$1"
|
||||||
|
local current_keychains=()
|
||||||
|
local line=""
|
||||||
|
|
||||||
|
while IFS= read -r line; do
|
||||||
|
line="${line#"${line%%[![:space:]]*}"}"
|
||||||
|
line="${line%\"}"
|
||||||
|
line="${line#\"}"
|
||||||
|
[[ -n "$line" ]] && current_keychains+=("$line")
|
||||||
|
done < <(security list-keychains -d user 2> /dev/null || true)
|
||||||
|
|
||||||
|
if printf '%s\n' "${current_keychains[@]}" | grep -Fx "$keychain_path" > /dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
security list-keychains -d user -s "$keychain_path" "${current_keychains[@]}" > /dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
decode_base64_to_file() {
|
||||||
|
local encoded="$1"
|
||||||
|
local destination="$2"
|
||||||
|
|
||||||
|
printf '%s' "$encoded" | base64 --decode > "$destination"
|
||||||
|
}
|
||||||
|
|
||||||
|
detect_identity() {
|
||||||
|
local policy="$1"
|
||||||
|
local prefix="$2"
|
||||||
|
local keychain_path="$3"
|
||||||
|
|
||||||
|
security find-identity -v -p "$policy" "$keychain_path" 2> /dev/null |
|
||||||
|
sed -n "s/.*\"\\($prefix.*\\)\"/\\1/p" |
|
||||||
|
head -1
|
||||||
|
}
|
||||||
|
|
||||||
|
require_env ATLAS_RELEASE_APP_CERT_P12_BASE64
|
||||||
|
require_env ATLAS_RELEASE_APP_CERT_P12_PASSWORD
|
||||||
|
require_env ATLAS_RELEASE_INSTALLER_CERT_P12_BASE64
|
||||||
|
require_env ATLAS_RELEASE_INSTALLER_CERT_P12_PASSWORD
|
||||||
|
require_env ATLAS_NOTARY_KEY_ID
|
||||||
|
require_env ATLAS_NOTARY_API_KEY_BASE64
|
||||||
|
|
||||||
|
KEYCHAIN_PATH="${ATLAS_RELEASE_KEYCHAIN_PATH:-${RUNNER_TEMP:-${TMPDIR:-/tmp}}/atlas-release.keychain-db}"
|
||||||
|
KEYCHAIN_PASSWORD="${ATLAS_RELEASE_KEYCHAIN_PASSWORD:-$(uuidgen | tr '[:upper:]' '[:lower:]')}"
|
||||||
|
NOTARY_PROFILE="${ATLAS_NOTARY_PROFILE:-atlas-release}"
|
||||||
|
|
||||||
|
tmpdir="$(mktemp -d "${TMPDIR:-/tmp}/atlas-release-signing.XXXXXX")"
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT
|
||||||
|
|
||||||
|
APP_CERT_PATH="$tmpdir/application-cert.p12"
|
||||||
|
INSTALLER_CERT_PATH="$tmpdir/installer-cert.p12"
|
||||||
|
NOTARY_KEY_PATH="$tmpdir/AuthKey.p8"
|
||||||
|
|
||||||
|
decode_base64_to_file "$ATLAS_RELEASE_APP_CERT_P12_BASE64" "$APP_CERT_PATH"
|
||||||
|
decode_base64_to_file "$ATLAS_RELEASE_INSTALLER_CERT_P12_BASE64" "$INSTALLER_CERT_PATH"
|
||||||
|
decode_base64_to_file "$ATLAS_NOTARY_API_KEY_BASE64" "$NOTARY_KEY_PATH"
|
||||||
|
|
||||||
|
if [[ -f "$KEYCHAIN_PATH" ]]; then
|
||||||
|
rm -f "$KEYCHAIN_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" > /dev/null
|
||||||
|
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
|
||||||
|
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
|
||||||
|
append_keychain_search_list "$KEYCHAIN_PATH"
|
||||||
|
|
||||||
|
security import "$APP_CERT_PATH" \
|
||||||
|
-k "$KEYCHAIN_PATH" \
|
||||||
|
-P "$ATLAS_RELEASE_APP_CERT_P12_PASSWORD" \
|
||||||
|
-f pkcs12 \
|
||||||
|
-A \
|
||||||
|
-T /usr/bin/codesign \
|
||||||
|
-T /usr/bin/security \
|
||||||
|
-T /usr/bin/productbuild > /dev/null
|
||||||
|
|
||||||
|
security import "$INSTALLER_CERT_PATH" \
|
||||||
|
-k "$KEYCHAIN_PATH" \
|
||||||
|
-P "$ATLAS_RELEASE_INSTALLER_CERT_P12_PASSWORD" \
|
||||||
|
-f pkcs12 \
|
||||||
|
-A \
|
||||||
|
-T /usr/bin/codesign \
|
||||||
|
-T /usr/bin/security \
|
||||||
|
-T /usr/bin/productbuild > /dev/null
|
||||||
|
|
||||||
|
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" > /dev/null
|
||||||
|
|
||||||
|
APP_IDENTITY="${ATLAS_CODESIGN_IDENTITY:-$(detect_identity codesigning 'Developer ID Application:' "$KEYCHAIN_PATH")}"
|
||||||
|
INSTALLER_IDENTITY="${ATLAS_INSTALLER_SIGN_IDENTITY:-$(detect_identity basic 'Developer ID Installer:' "$KEYCHAIN_PATH")}"
|
||||||
|
|
||||||
|
if [[ -z "$APP_IDENTITY" ]]; then
|
||||||
|
echo "Developer ID Application identity was not imported successfully." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "$INSTALLER_IDENTITY" ]]; then
|
||||||
|
echo "Developer ID Installer identity was not imported successfully." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
notarytool_args=(
|
||||||
|
store-credentials
|
||||||
|
"$NOTARY_PROFILE"
|
||||||
|
--key "$NOTARY_KEY_PATH"
|
||||||
|
--key-id "$ATLAS_NOTARY_KEY_ID"
|
||||||
|
--keychain "$KEYCHAIN_PATH"
|
||||||
|
--validate
|
||||||
|
)
|
||||||
|
|
||||||
|
if [[ -n "${ATLAS_NOTARY_ISSUER_ID:-}" ]]; then
|
||||||
|
notarytool_args+=(--issuer "$ATLAS_NOTARY_ISSUER_ID")
|
||||||
|
fi
|
||||||
|
|
||||||
|
xcrun notarytool "${notarytool_args[@]}" > /dev/null
|
||||||
|
|
||||||
|
write_env ATLAS_CODESIGN_KEYCHAIN "$KEYCHAIN_PATH"
|
||||||
|
write_env ATLAS_CODESIGN_IDENTITY "$APP_IDENTITY"
|
||||||
|
write_env ATLAS_INSTALLER_SIGN_IDENTITY "$INSTALLER_IDENTITY"
|
||||||
|
write_env ATLAS_NOTARY_PROFILE "$NOTARY_PROFILE"
|
||||||
|
write_env ATLAS_NOTARY_KEYCHAIN "$KEYCHAIN_PATH"
|
||||||
|
|
||||||
|
printf 'Configured Atlas release signing\n'
|
||||||
|
printf 'App identity: %s\n' "$APP_IDENTITY"
|
||||||
|
printf 'Installer identity: %s\n' "$INSTALLER_IDENTITY"
|
||||||
|
printf 'Notary profile: %s\n' "$NOTARY_PROFILE"
|
||||||
|
printf 'Keychain: %s\n' "$KEYCHAIN_PATH"
|
||||||
@@ -7,6 +7,7 @@ source "$ROOT_DIR/scripts/atlas/signing-common.sh"
|
|||||||
APP_IDENTITY_OVERRIDE="${ATLAS_CODESIGN_IDENTITY:-}"
|
APP_IDENTITY_OVERRIDE="${ATLAS_CODESIGN_IDENTITY:-}"
|
||||||
INSTALLER_IDENTITY_OVERRIDE="${ATLAS_INSTALLER_SIGN_IDENTITY:-}"
|
INSTALLER_IDENTITY_OVERRIDE="${ATLAS_INSTALLER_SIGN_IDENTITY:-}"
|
||||||
NOTARY_PROFILE_OVERRIDE="${ATLAS_NOTARY_PROFILE:-}"
|
NOTARY_PROFILE_OVERRIDE="${ATLAS_NOTARY_PROFILE:-}"
|
||||||
|
NOTARY_KEYCHAIN_OVERRIDE="${ATLAS_NOTARY_KEYCHAIN:-}"
|
||||||
|
|
||||||
codesign_output="$(security find-identity -v -p codesigning 2> /dev/null || true)"
|
codesign_output="$(security find-identity -v -p codesigning 2> /dev/null || true)"
|
||||||
basic_output="$(security find-identity -v -p basic 2> /dev/null || true)"
|
basic_output="$(security find-identity -v -p basic 2> /dev/null || true)"
|
||||||
@@ -26,6 +27,9 @@ printf '======================\n'
|
|||||||
printf 'Developer ID Application: %s\n' "${app_identity:-MISSING}"
|
printf 'Developer ID Application: %s\n' "${app_identity:-MISSING}"
|
||||||
printf 'Developer ID Installer: %s\n' "${installer_identity:-MISSING}"
|
printf 'Developer ID Installer: %s\n' "${installer_identity:-MISSING}"
|
||||||
printf 'Notary profile: %s\n' "${NOTARY_PROFILE_OVERRIDE:-MISSING}"
|
printf 'Notary profile: %s\n' "${NOTARY_PROFILE_OVERRIDE:-MISSING}"
|
||||||
|
if [[ -n "$NOTARY_KEYCHAIN_OVERRIDE" ]]; then
|
||||||
|
printf 'Notary keychain: %s\n' "$NOTARY_KEYCHAIN_OVERRIDE"
|
||||||
|
fi
|
||||||
if [[ -n "$local_identity" ]]; then
|
if [[ -n "$local_identity" ]]; then
|
||||||
printf 'Stable local app identity: %s\n' "$local_identity"
|
printf 'Stable local app identity: %s\n' "$local_identity"
|
||||||
else
|
else
|
||||||
@@ -47,7 +51,12 @@ if [[ -z "$NOTARY_PROFILE_OVERRIDE" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$NOTARY_PROFILE_OVERRIDE" ]]; then
|
if [[ -n "$NOTARY_PROFILE_OVERRIDE" ]]; then
|
||||||
if xcrun notarytool history --keychain-profile "$NOTARY_PROFILE_OVERRIDE" > /dev/null 2>&1; then
|
notarytool_args=(history --keychain-profile "$NOTARY_PROFILE_OVERRIDE")
|
||||||
|
if [[ -n "$NOTARY_KEYCHAIN_OVERRIDE" ]]; then
|
||||||
|
notarytool_args+=(--keychain "$NOTARY_KEYCHAIN_OVERRIDE")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if xcrun notarytool "${notarytool_args[@]}" > /dev/null 2>&1; then
|
||||||
echo '✓ notarytool profile is usable'
|
echo '✓ notarytool profile is usable'
|
||||||
else
|
else
|
||||||
echo '✗ notarytool profile could not be validated'
|
echo '✗ notarytool profile could not be validated'
|
||||||
|
|||||||
Reference in New Issue
Block a user