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
80 changes: 69 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1491,6 +1491,7 @@ jobs:
job_build_e2e_public_apps,
job_build_e2e_image,
job_e2e_tests,
build_packages,
publish_packages
]
if: always()
Expand All @@ -1504,15 +1505,73 @@ jobs:
run: |
echo "One of the dependent jobs have failed or been cancelled. You may need to re-run it." && exit 1

publish_packages:
# Build verification for @tryghost/* public apps.
# Runs on PRs and pushes. Intentionally has no `id-token: write` — PR-controlled
# code (nx build) must never execute in a job that can mint an npm OIDC token.
# The privileged publish flow lives in publish_packages below, gated to main pushes.
build_packages:
needs: [
job_setup,
job_lint,
job_unit-tests
]
name: Publish ${{ matrix.package_name }}
name: Build ${{ matrix.package_name }}
runs-on: ubuntu-latest
if: always() && github.repository == 'TryGhost/Ghost' && needs.job_setup.result == 'success' && needs.job_lint.result == 'success' && needs.job_unit-tests.result == 'success'
permissions:
contents: read
strategy:
matrix:
include:
- package_name: '@tryghost/activitypub'
package_path: 'apps/activitypub'
- package_name: '@tryghost/portal'
package_path: 'apps/portal'
- package_name: '@tryghost/sodo-search'
package_path: 'apps/sodo-search'
- package_name: '@tryghost/comments-ui'
package_path: 'apps/comments-ui'
- package_name: '@tryghost/signup-form'
package_path: 'apps/signup-form'
- package_name: '@tryghost/announcement-bar'
package_path: 'apps/announcement-bar'
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4
- name: Set up Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build the package
run: pnpm nx build ${{ matrix.package_name }}

# Publishes @tryghost/* public apps to npm via OIDC trusted publishing.
# Runs only on push-to-main — never on pull_request — so the `id-token: write`
# permission is never exposed to PR-controlled code (ref: ONC-1677).
publish_packages:
needs: [
job_setup,
job_lint,
job_unit-tests,
build_packages
]
name: Publish ${{ matrix.package_name }}
runs-on: ubuntu-latest
if: |
github.event_name != 'pull_request'
&& github.repository == 'TryGhost/Ghost'
&& needs.job_setup.outputs.is_main == 'true'
&& needs.job_setup.result == 'success'
&& needs.job_lint.result == 'success'
&& needs.job_unit-tests.result == 'success'
&& needs.build_packages.result == 'success'
permissions:
id-token: write
strategy:
Expand Down Expand Up @@ -1553,7 +1612,6 @@ jobs:
run: pnpm install --frozen-lockfile

- name: Check if version changed
if: needs.job_setup.outputs.is_main == 'true'
id: version_check
working-directory: ${{ matrix.package_path }}
run: |
Expand All @@ -1578,46 +1636,46 @@ jobs:
fi

- name: Build the package
if: steps.version_check.outputs.version_changed == 'true' || github.event_name == 'pull_request'
if: steps.version_check.outputs.version_changed == 'true'
run: pnpm nx build ${{ matrix.package_name }}

- name: Configure .npmrc
if: needs.job_setup.outputs.is_main == 'true' && steps.version_check.outputs.version_changed == 'true'
if: steps.version_check.outputs.version_changed == 'true'
run: |
echo "@tryghost:registry=https://registry.npmjs.org/" >> ~/.npmrc

# TODO: Check we can remove this once we update Node to v24
- name: Install v11 of NPM # We need this to install packages via OIDC.
if: needs.job_setup.outputs.is_main == 'true' && steps.version_check.outputs.version_changed == 'true'
if: steps.version_check.outputs.version_changed == 'true'
run: npm install -g npm@11

- name: Publish to npm
if: needs.job_setup.outputs.is_main == 'true' && steps.version_check.outputs.version_changed == 'true'
if: steps.version_check.outputs.version_changed == 'true'
working-directory: ${{ matrix.package_path }}
run: |
npm publish --access public

- name: Replace version placeholders in cdn-paths
id: cdn_paths
if: needs.job_setup.outputs.is_main == 'true' && steps.version_check.outputs.version_changed == 'true'
if: steps.version_check.outputs.version_changed == 'true'
run: |
cdn_paths="${{ matrix.cdn_paths }}"
echo "cdn_paths<<EOF" >> $GITHUB_OUTPUT
echo "$cdn_paths" | sed -e 's/CURRENT_MINOR/${{ steps.version_check.outputs.current_minor }}/g' -e 's/CURRENT_MAJOR/${{ steps.version_check.outputs.current_major }}/g' >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT

- name: Print cdn_paths
if: needs.job_setup.outputs.is_main == 'true' && steps.version_check.outputs.version_changed == 'true'
if: steps.version_check.outputs.version_changed == 'true'
run: echo "${{ steps.cdn_paths.outputs.cdn_paths }}"

- name: Wait before purging jsDelivr cache
if: needs.job_setup.outputs.is_main == 'true' && steps.version_check.outputs.version_changed == 'true' && matrix.package_name == '@tryghost/activitypub'
if: steps.version_check.outputs.version_changed == 'true' && matrix.package_name == '@tryghost/activitypub'
run: |
echo "Purging jsDelivr cache immediately after publishing a new version on NPM is unreliable. Waiting 1 minute before purging cache..."
sleep 60

- name: Purge jsDelivr cache
if: needs.job_setup.outputs.is_main == 'true' && steps.version_check.outputs.version_changed == 'true'
if: steps.version_check.outputs.version_changed == 'true'
uses: gacts/purge-jsdelivr-cache@8d92aea944f1a3e8ad70505379e1a8ac72d56b73 # v1
with:
url: ${{ steps.cdn_paths.outputs.cdn_paths }}
Expand Down
7 changes: 7 additions & 0 deletions apps/admin-x-design-system/src/assets/icons/beehiiv.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import SearchableSection from '../../searchable-section';

export const searchKeywords = {
integrations: ['advanced', 'integrations', 'zapier', 'slack', 'unsplash', 'first promoter', 'firstpromoter', 'pintura', 'disqus', 'analytics', 'ulysses', 'typeform', 'buffer', 'plausible', 'github', 'webhooks'],
migrationtools: ['import', 'export', 'migrate', 'substack', 'substack', 'migration', 'medium', 'wordpress', 'wp', 'squarespace'],
migrationtools: ['import', 'export', 'migrate', 'substack', 'substack', 'migration', 'medium', 'wordpress', 'wp', 'squarespace', 'beehiiv'],
codeInjection: ['advanced', 'code injection', 'head', 'footer'],
labs: ['advanced', 'labs', 'alpha', 'private', 'beta', 'flag', 'routes', 'redirect', 'translation', 'editor', 'portal'],
history: ['advanced', 'history', 'log', 'events', 'user events', 'staff', 'audit', 'action'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ const MigrationToolsImport: React.FC = () => {
title='Substack'
onClick={() => updateRoute({isExternal: true, route: '/migrate/substack'})}
/>
<ImportButton
icon={
<Icon className='w-auto' name='beehiiv' size={18} />
}
title='beehiiv'
onClick={() => updateRoute({isExternal: true, route: '/migrate/beehiiv'})}
/>
<ImportButton
icon={
<Icon className='w-auto' name='wordpress' size={18} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useRef, useState} from 'react';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {ColorPicker} from '@tryghost/shade/patterns';
import {Popover, PopoverContent, PopoverTrigger} from '@tryghost/shade/components';

Expand Down Expand Up @@ -59,16 +59,62 @@ const ColorPickerField: React.FC<ColorPickerFieldProps> = ({title, value, onChan
const normalizedValue = normalizeColorValue(value, accentColor, swatches);
const allowPickerChanges = useRef(false);
const suppressPickerChanges = useRef(false);
const earlyEscapeHandler = useRef<((event: KeyboardEvent) => void) | null>(null);
const selectedSwatch = swatches.find((swatch) => {
return swatch.value === value || (value && swatch.hex.toLowerCase() === value.toLowerCase());
});
const resetInteractionState = () => {
allowPickerChanges.current = false;
suppressPickerChanges.current = false;
};
const detachEarlyEscapeListener = useCallback(() => {
if (!earlyEscapeHandler.current) {
return;
}

window.removeEventListener('keydown', earlyEscapeHandler.current, true);
earlyEscapeHandler.current = null;
}, []);
const closePopover = () => {
detachEarlyEscapeListener();
resetInteractionState();
setOpen(false);
};
const attachEarlyEscapeListener = () => {
if (earlyEscapeHandler.current) {
return;
}

const handleEarlyEscape = (event: KeyboardEvent) => {
if (event.key !== 'Escape') {
return;
}

event.preventDefault();
event.stopPropagation();
closePopover();
};

earlyEscapeHandler.current = handleEarlyEscape;
window.addEventListener('keydown', handleEarlyEscape, true);
};

useEffect(() => {
return () => {
detachEarlyEscapeListener();
};
}, [detachEarlyEscapeListener]);

return (
<Popover
open={open}
onOpenChange={(nextOpen) => {
allowPickerChanges.current = false;
suppressPickerChanges.current = false;
resetInteractionState();
if (nextOpen) {
attachEarlyEscapeListener();
} else {
detachEarlyEscapeListener();
}
setOpen(nextOpen);
}}
>
Expand All @@ -94,7 +140,7 @@ const ColorPickerField: React.FC<ColorPickerFieldProps> = ({title, value, onChan
allowPickerChanges.current = false;
suppressPickerChanges.current = true;
onChange(swatchValue);
setOpen(false);
closePopover();
}}
>
{isTransparent(swatch.hex) && <TransparentIndicator />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ export {ImageCornersField} from './image-corners-field';
export {LinkColorField} from './link-color-field';
export {LinkStyleField} from './link-style-field';
export {SectionTitleColorField} from './section-title-color-field';
export {TitleAlignmentField} from './title-alignment-field';

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type {EmailDesignSettings} from './types';
export interface ResolvedEmailColors {
backgroundColor: string;
headerBackgroundColor: string;
postTitleColor: string;
sectionTitleColor: string;
buttonColor: string;
buttonTextColor: string | undefined;
Expand Down Expand Up @@ -66,17 +65,6 @@ function resolveLinkColor(value: string | null | undefined, accentColor: string,
return textColorForBackgroundColor(bgColor).hex();
}

function resolvePostTitleColor(value: string | null, accentColor: string, bgColor: string, headerBgColor: string): string {
if (VALID_HEX.test(value || '')) {
return value!;
}
if (value === 'accent') {
return accentColor;
}
const effectiveBg = headerBgColor === 'transparent' ? bgColor : headerBgColor;
return textColorForBackgroundColor(effectiveBg).hex();
}

function resolveSectionTitleColor(value: string | null, accentColor: string, bgColor: string): string {
if (VALID_HEX.test(value || '')) {
return value!;
Expand Down Expand Up @@ -110,7 +98,6 @@ export function resolveAllColors(settings: EmailDesignSettings, accentColor: str
return {
backgroundColor: bgColor,
headerBackgroundColor: headerBgColor,
postTitleColor: resolvePostTitleColor(settings.post_title_color, accentColor, bgColor, headerBgColor),
sectionTitleColor: resolveSectionTitleColor(settings.section_title_color, accentColor, bgColor),
buttonColor,
buttonTextColor: resolveButtonTextColor(settings.button_style, buttonColor),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export interface PersistedEmailDesignSettings {
export interface EmailDesignSettings {
background_color: string;
title_font_category: string;
title_font_weight: string;
Expand All @@ -14,13 +14,6 @@ export interface PersistedEmailDesignSettings {
divider_color: string | null;
}

export interface EmailDesignPreviewSettings {
post_title_color: string | null;
title_alignment: string;
}

export type EmailDesignSettings = PersistedEmailDesignSettings & EmailDesignPreviewSettings;

export const DEFAULT_EMAIL_DESIGN: EmailDesignSettings = {
background_color: 'light',
title_font_category: 'sans_serif',
Expand All @@ -34,7 +27,5 @@ export const DEFAULT_EMAIL_DESIGN: EmailDesignSettings = {
link_color: 'accent',
link_style: 'underline',
image_corners: 'square',
divider_color: null,
post_title_color: null,
title_alignment: 'center'
divider_color: null
};
Loading
Loading