mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
Allow RCs while Windows signing is pending
This commit is contained in:
@@ -7,8 +7,13 @@ on:
|
||||
description: 'Version number without the leading v'
|
||||
required: true
|
||||
type: string
|
||||
require_platform_signing:
|
||||
description: 'Require Developer ID/notarized macOS and Authenticode Windows agent binaries'
|
||||
require_macos_signing:
|
||||
description: 'Require Developer ID signed and notarized macOS agent binaries'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
require_windows_signing:
|
||||
description: 'Require Authenticode-signed Windows agent binaries'
|
||||
required: false
|
||||
default: false
|
||||
type: boolean
|
||||
@@ -26,7 +31,7 @@ permissions:
|
||||
jobs:
|
||||
signing-configuration:
|
||||
name: Verify Native Signing Configuration
|
||||
if: ${{ inputs.require_platform_signing }}
|
||||
if: ${{ inputs.require_macos_signing || inputs.require_windows_signing }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 2
|
||||
steps:
|
||||
@@ -40,18 +45,31 @@ jobs:
|
||||
APPLE_NOTARY_ISSUER_ID: ${{ secrets.APPLE_NOTARY_ISSUER_ID }}
|
||||
WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64: ${{ secrets.WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64 }}
|
||||
WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD }}
|
||||
REQUIRE_MACOS_SIGNING: ${{ inputs.require_macos_signing }}
|
||||
REQUIRE_WINDOWS_SIGNING: ${{ inputs.require_windows_signing }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
missing=0
|
||||
for name in \
|
||||
APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64 \
|
||||
APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD \
|
||||
APPLE_DEVELOPER_ID_APPLICATION_IDENTITY \
|
||||
APPLE_NOTARY_KEY_P8_BASE64 \
|
||||
APPLE_NOTARY_KEY_ID \
|
||||
APPLE_NOTARY_ISSUER_ID \
|
||||
WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64 \
|
||||
WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD; do
|
||||
required=()
|
||||
if [[ "$REQUIRE_MACOS_SIGNING" == "true" ]]; then
|
||||
required+=(
|
||||
APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64
|
||||
APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD
|
||||
APPLE_DEVELOPER_ID_APPLICATION_IDENTITY
|
||||
APPLE_NOTARY_KEY_P8_BASE64
|
||||
APPLE_NOTARY_KEY_ID
|
||||
APPLE_NOTARY_ISSUER_ID
|
||||
)
|
||||
fi
|
||||
if [[ "$REQUIRE_WINDOWS_SIGNING" == "true" ]]; then
|
||||
required+=(
|
||||
WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64
|
||||
WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD
|
||||
)
|
||||
else
|
||||
echo "::notice::Windows Authenticode is not required for this candidate."
|
||||
fi
|
||||
for name in "${required[@]}"; do
|
||||
if [ -z "${!name:-}" ]; then
|
||||
echo "::error::Missing required Actions secret ${name}."
|
||||
missing=1
|
||||
@@ -62,7 +80,7 @@ jobs:
|
||||
sign-macos-agent:
|
||||
name: Sign and Notarize macOS Agent
|
||||
needs: signing-configuration
|
||||
if: ${{ inputs.require_platform_signing && needs.signing-configuration.result == 'success' }}
|
||||
if: ${{ inputs.require_macos_signing && needs.signing-configuration.result == 'success' }}
|
||||
runs-on: macos-15
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
@@ -160,7 +178,7 @@ jobs:
|
||||
sign-windows-agent:
|
||||
name: Authenticode Sign Windows Agent
|
||||
needs: signing-configuration
|
||||
if: ${{ inputs.require_platform_signing && needs.signing-configuration.result == 'success' }}
|
||||
if: ${{ inputs.require_windows_signing && needs.signing-configuration.result == 'success' }}
|
||||
runs-on: windows-2025
|
||||
timeout-minutes: 25
|
||||
steps:
|
||||
@@ -225,7 +243,7 @@ jobs:
|
||||
build:
|
||||
name: Build and Validate Signed Candidate
|
||||
needs: [sign-macos-agent, sign-windows-agent]
|
||||
if: ${{ always() && (!inputs.require_platform_signing || (needs.sign-macos-agent.result == 'success' && needs.sign-windows-agent.result == 'success')) }}
|
||||
if: ${{ always() && (!inputs.require_macos_signing || needs.sign-macos-agent.result == 'success') && (!inputs.require_windows_signing || needs.sign-windows-agent.result == 'success') }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
outputs:
|
||||
@@ -288,14 +306,14 @@ jobs:
|
||||
install -m 0755 "${TMP_DIR}/syft" /usr/local/bin/syft
|
||||
|
||||
- name: Download signed macOS binaries
|
||||
if: ${{ inputs.require_platform_signing }}
|
||||
if: ${{ inputs.require_macos_signing }}
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: signed-macos-agent-${{ github.sha }}-${{ inputs.version }}
|
||||
path: native-agent-binaries
|
||||
|
||||
- name: Download signed Windows binaries
|
||||
if: ${{ inputs.require_platform_signing }}
|
||||
if: ${{ inputs.require_windows_signing }}
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
|
||||
with:
|
||||
name: signed-windows-agent-${{ github.sha }}-${{ inputs.version }}
|
||||
@@ -307,8 +325,9 @@ jobs:
|
||||
PULSE_LICENSE_PUBLIC_KEY: ${{ secrets.PULSE_LICENSE_PUBLIC_KEY }}
|
||||
PULSE_UPDATE_SIGNING_KEY: ${{ secrets.PULSE_UPDATE_SIGNING_KEY }}
|
||||
PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}
|
||||
PULSE_REQUIRE_PLATFORM_SIGNING: ${{ inputs.require_platform_signing }}
|
||||
PULSE_AGENT_NATIVE_BINARIES_DIR: ${{ inputs.require_platform_signing && format('{0}/native-agent-binaries', github.workspace) || '' }}
|
||||
PULSE_REQUIRE_MACOS_SIGNING: ${{ inputs.require_macos_signing }}
|
||||
PULSE_REQUIRE_WINDOWS_SIGNING: ${{ inputs.require_windows_signing }}
|
||||
PULSE_AGENT_NATIVE_BINARIES_DIR: ${{ (inputs.require_macos_signing || inputs.require_windows_signing) && format('{0}/native-agent-binaries', github.workspace) || '' }}
|
||||
|
||||
- name: Validate installer signing key pins
|
||||
env:
|
||||
|
||||
@@ -218,7 +218,8 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
version: ${{ needs.prepare.outputs.version }}
|
||||
require_platform_signing: true
|
||||
require_macos_signing: true
|
||||
require_windows_signing: ${{ needs.prepare.outputs.is_prerelease != 'true' }}
|
||||
|
||||
# Frontend checks run in parallel with backend tests
|
||||
frontend_checks:
|
||||
|
||||
@@ -68,7 +68,8 @@ jobs:
|
||||
secrets: inherit
|
||||
with:
|
||||
version: ${{ inputs.version }}
|
||||
require_platform_signing: true
|
||||
require_macos_signing: true
|
||||
require_windows_signing: true
|
||||
|
||||
dry-run:
|
||||
name: Preflight Release Checks (No Publish)
|
||||
|
||||
+27
@@ -131,3 +131,30 @@ demo runtime unchanged.
|
||||
Blocked only on the absent native platform signing credentials. Repository-side
|
||||
orchestration, diagnostics, timeout hardening, archive validation performance,
|
||||
and late backend-flake containment are implemented and covered by contracts.
|
||||
|
||||
## Prerelease Windows Signing Boundary (2026-07-10)
|
||||
|
||||
The first `v6.0.6-rc.1` publication attempt proved Apple signing and
|
||||
notarization credentials are configured, but Windows Authenticode credentials
|
||||
are not. Pulse Monitoring Ltd submitted the public community project to the
|
||||
SignPath Foundation open-source programme on 2026-07-10; approval and CI
|
||||
integration remain externally owned and asynchronous.
|
||||
|
||||
RC publication may proceed while that application is pending, provided all of
|
||||
the following remain true:
|
||||
|
||||
- macOS agent binaries are Developer ID signed and notarized;
|
||||
- Windows agent binaries retain the exact-SHA candidate, checksums, detached
|
||||
release signatures, and post-publication digest verification;
|
||||
- the RC release notes state explicitly that Windows binaries are not
|
||||
Authenticode-signed and may show an unknown-publisher warning; and
|
||||
- stable publication continues to require successful Windows Authenticode
|
||||
signing and verification.
|
||||
|
||||
The reusable candidate workflow therefore separates the macOS platform-signing
|
||||
requirement from the Windows Authenticode requirement. `create-release.yml`
|
||||
requires Windows signing for stable versions and relaxes only that requirement
|
||||
for recognized prerelease versions. `release-dry-run.yml` continues to require
|
||||
both platforms so the full stable-promotion path remains rehearsed and the
|
||||
`single-build-release-promotion-path` release gate remains blocked for stable
|
||||
release readiness until the external signing path is proven.
|
||||
|
||||
@@ -2301,6 +2301,17 @@ release-packet SBOM is absent so published RC/stable downloads can keep the
|
||||
updater and installer trust chain fail-closed instead of downgrading to
|
||||
checksum-only trust and can publish a shareable non-image software inventory
|
||||
alongside the signed binaries.
|
||||
The immutable candidate builder must model macOS Developer ID/notarization and
|
||||
Windows Authenticode as independent native-signing requirements rather than one
|
||||
all-or-nothing platform switch. Governed RC publication may require signed and
|
||||
notarized macOS agent binaries while Windows Authenticode approval is still an
|
||||
externally owned bounded residual, but only when the RC packet explicitly
|
||||
discloses the unsigned Windows publisher state and the Windows binaries retain
|
||||
the exact-SHA candidate, checksum, detached-signature, and post-publication
|
||||
digest controls. Stable publication and the stable-path dry-run must continue
|
||||
to require both native signing lanes. `scripts/build-release.sh` must replace
|
||||
only the native targets required by those independent inputs and must fail
|
||||
closed when a required native-binary directory or target is absent.
|
||||
Historical published-release repair must flow through
|
||||
`scripts/backfill-release-assets.sh` and
|
||||
`.github/workflows/backfill-release-assets.yml` or the canonical
|
||||
|
||||
@@ -44,6 +44,8 @@ behavior, and broad security and reliability hardening.
|
||||
fail fast when signing configuration is incomplete.
|
||||
- Docker updates now recreate the container instead of attempting a restart
|
||||
that cannot apply a new image.
|
||||
- Docker containers retain their grouped-by-host view and open configured web
|
||||
links consistently after REST resource snapshot hydration.
|
||||
- Docker and Kubernetes agents tolerate realistic clock skew when evaluating
|
||||
liveness, and posture alerts no longer ignore the intended guest-suppression
|
||||
rules.
|
||||
@@ -71,5 +73,10 @@ Pulse Mobile candidate builds with runtime version 1 receive the matching
|
||||
typed-action approval client through the candidate OTA channel; no public store
|
||||
rollout is part of this RC.
|
||||
|
||||
Windows Unified Agent binaries in this release candidate retain the same
|
||||
checksum and detached-signature verification used by `v6.0.5`, but they are
|
||||
not yet Authenticode-signed and Windows may show an unknown-publisher warning.
|
||||
Public Windows Authenticode signing remains required before stable promotion.
|
||||
|
||||
Paid Pulse Pro, Relay, and eligible legacy customers should continue to use the
|
||||
private download page and private runtime image for paid runtime features.
|
||||
|
||||
@@ -152,7 +152,13 @@ done
|
||||
# so the immutable candidate manifest covers the exact signed bytes users get.
|
||||
if [[ -n "${PULSE_AGENT_NATIVE_BINARIES_DIR:-}" ]]; then
|
||||
native_dir="${PULSE_AGENT_NATIVE_BINARIES_DIR}"
|
||||
native_targets=(darwin-amd64 darwin-arm64 windows-amd64 windows-arm64 windows-386)
|
||||
native_targets=()
|
||||
if [[ "${PULSE_REQUIRE_MACOS_SIGNING:-false}" == "true" ]]; then
|
||||
native_targets+=(darwin-amd64 darwin-arm64)
|
||||
fi
|
||||
if [[ "${PULSE_REQUIRE_WINDOWS_SIGNING:-false}" == "true" ]]; then
|
||||
native_targets+=(windows-amd64 windows-arm64 windows-386)
|
||||
fi
|
||||
for target in "${native_targets[@]}"; do
|
||||
filename="pulse-agent-${target}"
|
||||
if [[ "$target" == windows-* ]]; then
|
||||
@@ -164,9 +170,9 @@ if [[ -n "${PULSE_AGENT_NATIVE_BINARIES_DIR:-}" ]]; then
|
||||
fi
|
||||
cp "${native_dir}/${filename}" "${BUILD_DIR}/${filename}"
|
||||
done
|
||||
echo "Applied platform-native signed Unified Agent binaries."
|
||||
elif [[ "${PULSE_REQUIRE_PLATFORM_SIGNING:-false}" == "true" ]]; then
|
||||
echo "Error: platform signing is required but PULSE_AGENT_NATIVE_BINARIES_DIR is empty." >&2
|
||||
echo "Applied required platform-native signed Unified Agent binaries."
|
||||
elif [[ "${PULSE_REQUIRE_MACOS_SIGNING:-false}" == "true" || "${PULSE_REQUIRE_WINDOWS_SIGNING:-false}" == "true" ]]; then
|
||||
echo "Error: required native signing is enabled but PULSE_AGENT_NATIVE_BINARIES_DIR is empty." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
@@ -709,7 +709,8 @@ func TestAgentRuntimeImagePersistsAgentIdentityByDefault(t *testing.T) {
|
||||
func TestReleaseCandidateRequiresPlatformNativeAgentSigning(t *testing.T) {
|
||||
candidateWorkflowPath := repoFile(".github", "workflows", "build-release-candidate.yml")
|
||||
assertFileContainsAll(t, candidateWorkflowPath,
|
||||
`require_platform_signing:`,
|
||||
`require_macos_signing:`,
|
||||
`require_windows_signing:`,
|
||||
`sign-macos-agent:`,
|
||||
`codesign --force --timestamp --options runtime`,
|
||||
`xcrun notarytool submit`,
|
||||
@@ -729,13 +730,18 @@ func TestReleaseCandidateRequiresPlatformNativeAgentSigning(t *testing.T) {
|
||||
t.Fatal("bare command-line Mach-O binaries must not use Gatekeeper app assessment after notarization")
|
||||
}
|
||||
assertFileContainsAll(t, repoFile(".github", "workflows", "create-release.yml"),
|
||||
`require_platform_signing: true`,
|
||||
`require_macos_signing: true`,
|
||||
`require_windows_signing: ${{ needs.prepare.outputs.is_prerelease != 'true' }}`,
|
||||
)
|
||||
assertFileContainsAll(t, repoFile("scripts", "build-release.sh"),
|
||||
`PULSE_AGENT_NATIVE_BINARIES_DIR`,
|
||||
`native_targets=(darwin-amd64 darwin-arm64 windows-amd64 windows-arm64 windows-386)`,
|
||||
`Applied platform-native signed Unified Agent binaries.`,
|
||||
`platform signing is required but PULSE_AGENT_NATIVE_BINARIES_DIR is empty.`,
|
||||
`native_targets=()`,
|
||||
`PULSE_REQUIRE_MACOS_SIGNING:-false`,
|
||||
`native_targets+=(darwin-amd64 darwin-arm64)`,
|
||||
`PULSE_REQUIRE_WINDOWS_SIGNING:-false`,
|
||||
`native_targets+=(windows-amd64 windows-arm64 windows-386)`,
|
||||
`Applied required platform-native signed Unified Agent binaries.`,
|
||||
`required native signing is enabled but PULSE_AGENT_NATIVE_BINARIES_DIR is empty.`,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -486,7 +486,8 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
self.assertIn("rc-to-ga-rehearsal-summary", workflow)
|
||||
self.assertIn("build_release_candidate:", workflow)
|
||||
self.assertIn("if: ${{ inputs.version != '' }}", workflow)
|
||||
self.assertIn("require_platform_signing: true", workflow)
|
||||
self.assertIn("require_macos_signing: true", workflow)
|
||||
self.assertIn("require_windows_signing: true", workflow)
|
||||
self.assertNotIn("if: ${{ github.event_name == 'workflow_dispatch' }}", workflow)
|
||||
self.assertIn("record_rc_to_ga_rehearsal.py --run-id ${{ github.run_id }}", workflow)
|
||||
self.assertIn("rc-to-ga-promotion-readiness-rehearsal-<record-date>.md", workflow)
|
||||
@@ -623,6 +624,9 @@ class ReleasePromotionPolicyTest(unittest.TestCase):
|
||||
self.assertIn("timeout-minutes: 60", candidate_workflow)
|
||||
self.assertIn("Verify Native Signing Configuration", candidate_workflow)
|
||||
self.assertEqual(candidate_workflow.count("needs: signing-configuration"), 2)
|
||||
self.assertIn("require_windows_signing: ${{ needs.prepare.outputs.is_prerelease != 'true' }}", content)
|
||||
self.assertIn('if [[ "$REQUIRE_WINDOWS_SIGNING" == "true" ]]', candidate_workflow)
|
||||
self.assertIn("inputs.require_windows_signing", candidate_workflow)
|
||||
for signing_secret in (
|
||||
"APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64",
|
||||
"APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD",
|
||||
|
||||
Reference in New Issue
Block a user