Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/workflows/desktop_publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ jobs:
echo "::error::Dry-run workflow ID must be a positive integer"
exit 1
fi
if [[ "$INPUT_INCLUDE_WINDOWS" != "true" ]]; then
echo "::error::Stable releases must include Windows for automatic Microsoft Store submission"
exit 1
fi

VERSION="$INPUT_VERSION"
CANDIDATE_SHA="${INPUT_CANDIDATE_SHA,,}"
Expand Down Expand Up @@ -560,6 +564,38 @@ jobs:
echo "- Silent install argument: \`/S\`"
} >> "$GITHUB_STEP_SUMMARY"

store-publish:
needs: [parse, gh-release]
if: ${{ !cancelled() && needs.parse.result == 'success' && needs.gh-release.result == 'success' }}
permissions:
contents: read
uses: ./.github/workflows/desktop_store_publish.yaml
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
with:
version: ${{ needs.parse.outputs.version }}
candidate_sha: ${{ needs.parse.outputs.candidate_sha }}
include_macos: true
include_windows: true
submit_to_stores: true
secrets:
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPSTORE_API_KEY_ID: ${{ secrets.APPSTORE_API_KEY_ID }}
APPSTORE_API_PRIVATE_KEY: ${{ secrets.APPSTORE_API_PRIVATE_KEY }}
APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }}
AZURE_AD_APPLICATION_SECRET: ${{ secrets.AZURE_AD_APPLICATION_SECRET }}
CN_API_KEY: ${{ secrets.CN_API_KEY }}
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
MAC_APP_STORE_APPLICATION_CERTIFICATE: ${{ secrets.MAC_APP_STORE_APPLICATION_CERTIFICATE }}
MAC_APP_STORE_APPLICATION_CERTIFICATE_PASSWORD: ${{ secrets.MAC_APP_STORE_APPLICATION_CERTIFICATE_PASSWORD }}
MAC_APP_STORE_INSTALLER_CERTIFICATE: ${{ secrets.MAC_APP_STORE_INSTALLER_CERTIFICATE }}
MAC_APP_STORE_INSTALLER_CERTIFICATE_PASSWORD: ${{ secrets.MAC_APP_STORE_INSTALLER_CERTIFICATE_PASSWORD }}
MAC_APP_STORE_PROVISIONING_PROFILE: ${{ secrets.MAC_APP_STORE_PROVISIONING_PROFILE }}
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
SELLER_ID: ${{ secrets.SELLER_ID }}
SENTRY_DSN_HYPRNOTE_2: ${{ secrets.SENTRY_DSN_HYPRNOTE_2 }}
VITE_PRO_PRODUCT_ID: ${{ secrets.VITE_PRO_PRODUCT_ID }}
VITE_SUPABASE_ANON_KEY: ${{ secrets.VITE_SUPABASE_ANON_KEY }}
VITE_SUPABASE_URL: ${{ secrets.VITE_SUPABASE_URL }}

linux-package-bump:
needs: [parse, gh-release]
if: ${{ !cancelled() && needs.parse.outputs.include_linux == 'true' && needs.gh-release.result == 'success' }}
Expand Down
59 changes: 59 additions & 0 deletions .github/workflows/desktop_store_publish.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,65 @@
name: Desktop Store Publish

on:
workflow_call:
inputs:
version:
description: "Stable desktop version to release (e.g., 1.4.12)"
required: true
type: string
candidate_sha:
description: "Exact 40-character commit tagged for the stable release"
required: true
type: string
include_macos:
description: "Build the signed Mac App Store package"
type: boolean
default: true
include_windows:
description: "Prepare the Microsoft Store package update"
type: boolean
default: true
submit_to_stores:
description: "Upload to App Store Connect and submit the Microsoft Store update"
type: boolean
default: false
secrets:
APPLE_TEAM_ID:
required: false
APPSTORE_API_KEY_ID:
required: false
APPSTORE_API_PRIVATE_KEY:
required: false
APPSTORE_ISSUER_ID:
required: false
AZURE_AD_APPLICATION_SECRET:
required: false
CN_API_KEY:
required: false
KEYCHAIN_PASSWORD:
required: false
MAC_APP_STORE_APPLICATION_CERTIFICATE:
required: false
MAC_APP_STORE_APPLICATION_CERTIFICATE_PASSWORD:
required: false
MAC_APP_STORE_INSTALLER_CERTIFICATE:
required: false
MAC_APP_STORE_INSTALLER_CERTIFICATE_PASSWORD:
required: false
MAC_APP_STORE_PROVISIONING_PROFILE:
required: false
POSTHOG_API_KEY:
required: false
SELLER_ID:
required: false
SENTRY_DSN_HYPRNOTE_2:
required: false
VITE_PRO_PRODUCT_ID:
required: false
VITE_SUPABASE_ANON_KEY:
required: false
VITE_SUPABASE_URL:
required: false
workflow_dispatch:
inputs:
version:
Expand Down
69 changes: 69 additions & 0 deletions scripts/desktop-release-provenance.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,75 @@ test("repository release workflows match the authored release plan", async () =>
verifyWorkflowPlatformCoverage({ publishWorkflow, cdWorkflow });
});

test("stable desktop releases submit both store packages", async () => {
const [publishWorkflow, storeWorkflow] = await Promise.all([
readFile(".github/workflows/desktop_publish.yaml", "utf8"),
readFile(".github/workflows/desktop_store_publish.yaml", "utf8"),
]);

assert.match(storeWorkflow, /\n workflow_call:\n/);
assert.match(
publishWorkflow,
/Stable releases must include Windows for automatic Microsoft Store submission/,
);

const jobStart = publishWorkflow.indexOf("\n store-publish:\n");
assert.notEqual(jobStart, -1, "missing store-publish job");
const remainingWorkflow = publishWorkflow.slice(jobStart + 1);
const nextJob = remainingWorkflow.slice(1).search(/\n [a-z][a-z0-9-]*:\n/);
const storePublishJob =
nextJob === -1
? remainingWorkflow
: remainingWorkflow.slice(0, nextJob + 1);

assert.match(storePublishJob, /needs: \[parse, gh-release\]/);
assert.match(
storePublishJob,
/uses: \.\/\.github\/workflows\/desktop_store_publish\.yaml/,
);
assert.match(storePublishJob, /include_macos: true/);
assert.match(storePublishJob, /include_windows: true/);
assert.match(storePublishJob, /submit_to_stores: true/);
assert.doesNotMatch(storePublishJob, /secrets: inherit/);

const expectedSecrets = [
"APPLE_TEAM_ID",
"APPSTORE_API_KEY_ID",
"APPSTORE_API_PRIVATE_KEY",
"APPSTORE_ISSUER_ID",
"AZURE_AD_APPLICATION_SECRET",
"CN_API_KEY",
"KEYCHAIN_PASSWORD",
"MAC_APP_STORE_APPLICATION_CERTIFICATE",
"MAC_APP_STORE_APPLICATION_CERTIFICATE_PASSWORD",
"MAC_APP_STORE_INSTALLER_CERTIFICATE",
"MAC_APP_STORE_INSTALLER_CERTIFICATE_PASSWORD",
"MAC_APP_STORE_PROVISIONING_PROFILE",
"POSTHOG_API_KEY",
"SELLER_ID",
"SENTRY_DSN_HYPRNOTE_2",
"VITE_PRO_PRODUCT_ID",
"VITE_SUPABASE_ANON_KEY",
"VITE_SUPABASE_URL",
];
const declaredSecrets = [
...storeWorkflow.matchAll(
/^ ([A-Z0-9_]+):\n required: false$/gm,
),
].map((match) => match[1]);
const forwardedSecrets = [
...storePublishJob.matchAll(
/^ ([A-Z0-9_]+): \$\{\{ secrets\.([A-Z0-9_]+) \}\}$/gm,
),
].map((match) => {
assert.equal(match[1], match[2]);
return match[1];
});

assert.deepEqual(declaredSecrets, expectedSecrets);
assert.deepEqual(forwardedSecrets, expectedSecrets);
});

test("binds every release asset to a candidate run and detects replacement", async () => {
const directory = await mkdtemp(
path.join(os.tmpdir(), "anarlog-release-provenance-"),
Expand Down
Loading