chore(marketing): remove the private marketing submodule (#2732)

This commit is contained in:
Hampus
2026-09-13 16:37:55 +02:00
committed by GitHub
parent 38b7c63431
commit 580401d2dc
71 changed files with 7 additions and 96223 deletions
+1 -1
View File
@@ -14,7 +14,7 @@
"DOCKER_HOST": "unix:///var/run/docker.sock" "DOCKER_HOST": "unix:///var/run/docker.sock"
}, },
"runServices": ["workspace", "postgres", "valkey", "nats", "livekit", "meilisearch", "mailpit"], "runServices": ["workspace", "postgres", "valkey", "nats", "livekit", "meilisearch", "mailpit"],
"forwardPorts": [3000, 8088, 8080, 8771, 8082, 8773, 3010, 3020, 8333], "forwardPorts": [3000, 8088, 8080, 8771, 8082, 8773, 3020, 8333],
"portsAttributes": { "portsAttributes": {
"8088": { "8088": {
"label": "Fluxer dev proxy", "label": "Fluxer dev proxy",
-1
View File
@@ -256,7 +256,6 @@ services:
- "127.0.0.1:${FLUXER_DEV_GATEWAY_PORT:-8771}:8771" - "127.0.0.1:${FLUXER_DEV_GATEWAY_PORT:-8771}:8771"
- "127.0.0.1:${FLUXER_DEV_MEDIA_PROXY_PORT:-8082}:8082" - "127.0.0.1:${FLUXER_DEV_MEDIA_PROXY_PORT:-8082}:8082"
- "127.0.0.1:${FLUXER_DEV_APP_PROXY_PORT:-8773}:8773" - "127.0.0.1:${FLUXER_DEV_APP_PROXY_PORT:-8773}:8773"
- "127.0.0.1:${FLUXER_DEV_MARKETING_PORT:-3010}:3010"
- "127.0.0.1:${FLUXER_DEV_ADMIN_PORT:-3020}:3020" - "127.0.0.1:${FLUXER_DEV_ADMIN_PORT:-3020}:3020"
- "127.0.0.1:${FLUXER_DEV_SEAWEEDFS_S3_PORT:-3900}:8333" - "127.0.0.1:${FLUXER_DEV_SEAWEEDFS_S3_PORT:-3900}:8333"
depends_on: depends_on:
-1
View File
@@ -9,7 +9,6 @@
**/.git/** **/.git/**
/.github/ /.github/
/.pnpm-store/ /.pnpm-store/
/fluxer_marketing
**/.env **/.env
**/.env.*.local **/.env.*.local
-5
View File
@@ -1,7 +1,2 @@
/.github/CODEOWNERS @fluxerapp/developers /.github/CODEOWNERS @fluxerapp/developers
/.github/workflows/ @fluxerapp/developers /.github/workflows/ @fluxerapp/developers
/fluxer_marketing @fluxerapp/developers
/.gitmodules @fluxerapp/developers
/.github/workflows/dispatch-private-marketing-build.yaml @fluxerapp/developers
/packages/i18n/marketing/ @fluxerapp/developers
/scripts/setup-private-marketing.sh @fluxerapp/developers
-18
View File
@@ -89,21 +89,3 @@ Submit translations through [Weblate](https://weblate.fluxer.tools), not through
All repository activity is governed by the [Code of Conduct](https://github.com/fluxerapp/fluxer/blob/main/.github/CODE_OF_CONDUCT.md). All repository activity is governed by the [Code of Conduct](https://github.com/fluxerapp/fluxer/blob/main/.github/CODE_OF_CONDUCT.md).
Fluxer is distributed under the [GNU Affero General Public License, version 3.0 or later](https://github.com/fluxerapp/fluxer/blob/main/LICENSE). By adding a DCO sign-off, you certify that you have the right to submit the contribution under that licence. Fluxer is distributed under the [GNU Affero General Public License, version 3.0 or later](https://github.com/fluxerapp/fluxer/blob/main/LICENSE). By adding a DCO sign-off, you certify that you have the right to submit the contribution under that licence.
## Private marketing project
The marketing implementation is maintained in a private repository at the `fluxer_marketing` submodule path. The public workspace, bootstrap, checks, and development stack work without initializing it.
Authorized maintainers can initialize only that submodule and install its independent dependencies:
```sh
./scripts/setup-private-marketing.sh
pnpm --dir fluxer_marketing install --frozen-lockfile
cargo metadata --locked --manifest-path fluxer_marketing/Cargo.toml
```
To run the private marketing service in the local development stack and direct application links to it, add this override to the ignored `config/env/local.env` file:
```sh
FLUXER_MARKETING_ENDPOINT=http://localhost:8088/marketing
```
-5
View File
@@ -22,11 +22,6 @@ f:docs:
f:gateway: f:gateway:
- changed-files: - changed-files:
- any-glob-to-any-file: fluxer_gateway/**/* - any-glob-to-any-file: fluxer_gateway/**/*
f:marketing:
- changed-files:
- any-glob-to-any-file:
- fluxer_marketing
- packages/i18n/marketing/**/*
f:media_proxy: f:media_proxy:
- changed-files: - changed-files:
- any-glob-to-any-file: fluxer_media_proxy/**/* - any-glob-to-any-file: fluxer_media_proxy/**/*
@@ -1,230 +0,0 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
name: Dispatch private marketing build
on:
push:
branches:
- main
paths:
- fluxer_marketing
- Cargo.toml
- fluxer_common/**
- packages/fonts/manifest.json
- packages/fonts/NOTICE.md
- packages/fonts/LICENSE-IBM-PLEX.txt
- packages/fonts/css/locale-fallbacks.css
- packages/fonts/files/FluxerSans/**
- packages/fonts/files/FluxerMono/**
- packages/fonts/marketing/**
- packages/i18n/marketing/**
- fluxer_static/marketing/branding/**
- .github/workflows/dispatch-private-marketing-build.yaml
permissions:
actions: read
contents: read
concurrency:
group: private-marketing-dispatch
cancel-in-progress: false
jobs:
metadata:
name: resolve exact private build metadata
if: github.repository == 'fluxerapp/fluxer'
runs-on: ubuntu-24.04
timeout-minutes: 5
outputs:
parent_sha: ${{ steps.inputs.outputs.parent_sha }}
gitlink_sha: ${{ steps.inputs.outputs.gitlink_sha }}
build_version: ${{ steps.inputs.outputs.build_version }}
correlation_id: ${{ steps.inputs.outputs.correlation_id }}
steps:
- name: Create token
id: create-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
client-id: ${{ vars.FLUXER_CI_APP_ID }}
private-key: ${{ secrets.FLUXER_CI_APP_KEY }}
owner: fluxerapp
repositories: fluxer
permission-contents: read
- name: Resolve trusted build inputs
id: inputs
env:
EVENT_AFTER: ${{ github.event.after }}
GH_TOKEN: ${{ steps.create-token.outputs.token }}
PARENT_SHA: ${{ github.sha }}
PUBLIC_REPOSITORY: ${{ github.repository }}
RUN_ID: ${{ github.run_id }}
RUN_ATTEMPT: ${{ github.run_attempt }}
run: |
set -euo pipefail
[[ "$GITHUB_EVENT_NAME" == "push" ]]
[[ "$GITHUB_REF" == "refs/heads/main" ]]
[[ "$PUBLIC_REPOSITORY" == "fluxerapp/fluxer" ]]
[[ "$PARENT_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$EVENT_AFTER" == "$PARENT_SHA" ]]
[[ "$RUN_ID" =~ ^[1-9][0-9]*$ ]]
[[ "$RUN_ATTEMPT" =~ ^[1-9][0-9]*$ ]]
(( 10#$RUN_ATTEMPT <= 10 ))
main_sha="$(gh api "repos/$PUBLIC_REPOSITORY/git/ref/heads/main" --jq .object.sha)"
[[ "$main_sha" =~ ^[0-9a-f]{40}$ ]]
main_comparison="$(gh api "repos/$PUBLIC_REPOSITORY/compare/$PARENT_SHA...$main_sha")"
main_status="$(jq -r .status <<<"$main_comparison")"
[[ "$main_status" == "identical" || "$main_status" == "ahead" ]]
[[ "$(jq -r .merge_base_commit.sha <<<"$main_comparison")" == "$PARENT_SHA" ]]
commit="$(gh api "repos/$PUBLIC_REPOSITORY/git/commits/$PARENT_SHA")"
[[ "$(jq -r .sha <<<"$commit")" == "$PARENT_SHA" ]]
tree_sha="$(jq -r .tree.sha <<<"$commit")"
[[ "$tree_sha" =~ ^[0-9a-f]{40}$ ]]
entry="$(
gh api "repos/$PUBLIC_REPOSITORY/git/trees/$tree_sha" |
jq -cer '[.tree[] | select(.path == "fluxer_marketing")] | if length == 1 then .[0] else error("expected exactly one marketing gitlink") end'
)"
mode="$(jq -r .mode <<<"$entry")"
type="$(jq -r .type <<<"$entry")"
gitlink_sha="$(jq -r .sha <<<"$entry")"
path="$(jq -r .path <<<"$entry")"
if [[ "$mode" != "160000" || "$type" != "commit" || "$path" != "fluxer_marketing" || ! "$gitlink_sha" =~ ^[0-9a-f]{40}$ ]]; then
echo "::error::Public parent does not contain a valid fluxer_marketing gitlink."
exit 1
fi
run="$(gh api "repos/$PUBLIC_REPOSITORY/actions/runs/$RUN_ID")"
[[ "$(jq -r .id <<<"$run")" == "$RUN_ID" ]]
[[ "$(jq -r .run_attempt <<<"$run")" == "$RUN_ATTEMPT" ]]
[[ "$(jq -r .event <<<"$run")" == "push" ]]
[[ "$(jq -r .head_sha <<<"$run")" == "$PARENT_SHA" ]]
run_created_at="$(jq -r .created_at <<<"$run")"
[[ "$run_created_at" =~ ^[1-9][0-9]{3}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$ ]]
run_created_epoch="$(date -u -d "$run_created_at" +%s)"
[[ "$run_created_epoch" =~ ^[1-9][0-9]*$ ]]
build_epoch=$((run_created_epoch + 10#$RUN_ATTEMPT - 1))
read -r year month day time_segment <<<"$(date -u -d "@$build_epoch" '+%Y %m %d %H%M%S')"
month="$((10#$month))"
micro="$((10#$time_segment))"
build_version="$year.$month$day.$micro"
[[ "$build_version" =~ ^[1-9][0-9]{3}\.[1-9][0-9]{2,3}\.([0-9]|[1-9][0-9]{0,5})$ ]]
correlation_id="public-${RUN_ID}-${RUN_ATTEMPT}"
[[ "$correlation_id" =~ ^[A-Za-z0-9._:-]{1,64}$ ]]
{
echo "parent_sha=$PARENT_SHA"
echo "gitlink_sha=$gitlink_sha"
echo "build_version=$build_version"
echo "correlation_id=$correlation_id"
} >>"$GITHUB_OUTPUT"
dispatch:
name: dispatch exact private build
needs: metadata
runs-on: ubuntu-24.04
timeout-minutes: 65
environment: private-marketing-dispatch
permissions: {}
steps:
- name: Validate trusted build inputs
env:
DISPATCH_ENABLED: ${{ vars.MARKETING_DISPATCH_ENABLED }}
EXPECTED_PARENT_SHA: ${{ github.sha }}
EXPECTED_CORRELATION_ID: public-${{ github.run_id }}-${{ github.run_attempt }}
PARENT_SHA: ${{ needs.metadata.outputs.parent_sha }}
GITLINK_SHA: ${{ needs.metadata.outputs.gitlink_sha }}
BUILD_VERSION: ${{ needs.metadata.outputs.build_version }}
CORRELATION_ID: ${{ needs.metadata.outputs.correlation_id }}
run: |
set -euo pipefail
[[ "$GITHUB_EVENT_NAME" == "push" ]]
[[ "$GITHUB_REF" == "refs/heads/main" ]]
[[ "$GITHUB_REPOSITORY" == "fluxerapp/fluxer" ]]
[[ "$PARENT_SHA" == "$EXPECTED_PARENT_SHA" ]]
[[ "$PARENT_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$GITLINK_SHA" =~ ^[0-9a-f]{40}$ ]]
[[ "$BUILD_VERSION" =~ ^[1-9][0-9]{3}\.[1-9][0-9]{2,3}\.([0-9]|[1-9][0-9]{0,5})$ ]]
[[ "$CORRELATION_ID" == "$EXPECTED_CORRELATION_ID" ]]
[[ "$CORRELATION_ID" =~ ^[A-Za-z0-9._:-]{1,64}$ ]]
if [[ "$DISPATCH_ENABLED" != "true" ]]; then
echo "::error::Private marketing dispatch is intentionally disabled until the package cutover guard completes."
exit 1
fi
- name: Create private dispatch token
id: private-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
client-id: ${{ vars.FLUXER_CI_APP_ID }}
private-key: ${{ secrets.FLUXER_CI_APP_KEY }}
owner: fluxerapp
repositories: marketing
permission-actions: write
- name: Dispatch exact private build
env:
GH_TOKEN: ${{ steps.private-token.outputs.token }}
PARENT_SHA: ${{ needs.metadata.outputs.parent_sha }}
GITLINK_SHA: ${{ needs.metadata.outputs.gitlink_sha }}
BUILD_VERSION: ${{ needs.metadata.outputs.build_version }}
CORRELATION_ID: ${{ needs.metadata.outputs.correlation_id }}
run: |
set -euo pipefail
gh api --method POST repos/fluxerapp/marketing/actions/workflows/build-marketing.yaml/dispatches \
--field ref=main \
--field "inputs[parent_sha]=$PARENT_SHA" \
--field "inputs[gitlink_sha]=$GITLINK_SHA" \
--field "inputs[build_version]=$BUILD_VERSION" \
--field "inputs[correlation_id]=$CORRELATION_ID"
- name: Wait for private build conclusion
env:
GH_TOKEN: ${{ steps.private-token.outputs.token }}
PARENT_SHA: ${{ needs.metadata.outputs.parent_sha }}
GITLINK_SHA: ${{ needs.metadata.outputs.gitlink_sha }}
BUILD_VERSION: ${{ needs.metadata.outputs.build_version }}
CORRELATION_ID: ${{ needs.metadata.outputs.correlation_id }}
run: |
set -euo pipefail
expected_title="marketing-build correlation=$CORRELATION_ID parent=$PARENT_SHA gitlink=$GITLINK_SHA version=$BUILD_VERSION"
deadline=$((SECONDS + 3600))
run_id=""
while (( SECONDS < deadline )); do
runs="$(gh api "repos/fluxerapp/marketing/actions/workflows/build-marketing.yaml/runs?event=workflow_dispatch&per_page=100" --jq '[.workflow_runs[] | {id, event, display_title, status, conclusion}]')"
matches="$(jq --arg title "$expected_title" '[.[] | select(.event == "workflow_dispatch" and .display_title == $title)]' <<<"$runs")"
count="$(jq 'length' <<<"$matches")"
if [[ "$count" == "1" ]]; then
run_id="$(jq -r '.[0].id' <<<"$matches")"
break
fi
if [[ "$count" != "0" ]]; then
echo "::error::Private build correlation matched multiple workflow runs."
exit 1
fi
sleep 10
done
if [[ -z "$run_id" ]]; then
echo "::error::Timed out waiting for the private build dispatch to appear."
exit 1
fi
while (( SECONDS < deadline )); do
runs="$(gh api "repos/fluxerapp/marketing/actions/workflows/build-marketing.yaml/runs?event=workflow_dispatch&per_page=100" --jq '[.workflow_runs[] | {id, event, display_title, status, conclusion}]')"
matches="$(jq --arg title "$expected_title" '[.[] | select(.event == "workflow_dispatch" and .display_title == $title)]' <<<"$runs")"
if [[ "$(jq 'length' <<<"$matches")" != "1" || "$(jq -r '.[0].id' <<<"$matches")" != "$run_id" ]]; then
echo "::error::Private build correlation is missing or ambiguous."
exit 1
fi
run="$(jq '.[0]' <<<"$matches")"
status="$(jq -r '.status' <<<"$run")"
conclusion="$(jq -r '.conclusion // empty' <<<"$run")"
if [[ "$status" == "completed" ]]; then
if [[ "$conclusion" != "success" ]]; then
echo "::error::Private marketing build concluded with $conclusion."
exit 1
fi
echo "Private marketing build completed successfully."
exit 0
fi
sleep 15
done
echo "::error::Timed out waiting for the private marketing build."
exit 1
+2 -2
View File
@@ -77,14 +77,14 @@ jobs:
GH_TOKEN: ${{ steps.create-token.outputs.token }} GH_TOKEN: ${{ steps.create-token.outputs.token }}
run: | run: |
set -euo pipefail set -euo pipefail
if [[ -z "$(git status --porcelain -- fluxer_app/src/features/i18n/locales packages/i18n/marketing packages/errors/src/i18n fluxer_api/pkgs/email/src/email_i18n fluxer_api/src/api/content_i18n)" ]]; then if [[ -z "$(git status --porcelain -- fluxer_app/src/features/i18n/locales packages/errors/src/i18n fluxer_api/pkgs/email/src/email_i18n fluxer_api/src/api/content_i18n)" ]]; then
echo "No generated catalog changes." echo "No generated catalog changes."
exit 0 exit 0
fi fi
git config user.name "fluxer-ci[bot]" git config user.name "fluxer-ci[bot]"
git config user.email "${{ vars.FLUXER_CI_APP_USER_ID }}+fluxer-ci[bot]@users.noreply.github.com" git config user.email "${{ vars.FLUXER_CI_APP_USER_ID }}+fluxer-ci[bot]@users.noreply.github.com"
git add fluxer_app/src/features/i18n/locales packages/i18n/marketing packages/errors/src/i18n fluxer_api/pkgs/email/src/email_i18n fluxer_api/src/api/content_i18n git add fluxer_app/src/features/i18n/locales packages/errors/src/i18n fluxer_api/pkgs/email/src/email_i18n fluxer_api/src/api/content_i18n
git commit -m "i18n: compile Weblate catalogs" git commit -m "i18n: compile Weblate catalogs"
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
git push origin "HEAD:$WEBLATE_BRANCH" git push origin "HEAD:$WEBLATE_BRANCH"
-4
View File
@@ -1,4 +0,0 @@
[submodule "fluxer_marketing"]
path = fluxer_marketing
url = https://github.com/fluxerapp/marketing.git
update = none
-1
View File
@@ -18,7 +18,6 @@ members = [
"fluxer_recon", "fluxer_recon",
] ]
exclude = [ exclude = [
"fluxer_marketing",
"packages/markdown_parser/rust/fuzz", "packages/markdown_parser/rust/fuzz",
] ]
resolver = "2" resolver = "2"
-4
View File
@@ -95,10 +95,6 @@ FLUXER_ADMIN_BASE_PATH=/admin
FLUXER_ADMIN_SECRET_KEY_BASE=dev-admin-secret-key-base FLUXER_ADMIN_SECRET_KEY_BASE=dev-admin-secret-key-base
FLUXER_ADMIN_OAUTH_CLIENT_SECRET=dev-admin-oauth-secret FLUXER_ADMIN_OAUTH_CLIENT_SECRET=dev-admin-oauth-secret
FLUXER_ADMIN_OAUTH_REDIRECT_URI=http://localhost:8088/admin/oauth2_callback FLUXER_ADMIN_OAUTH_REDIRECT_URI=http://localhost:8088/admin/oauth2_callback
FLUXER_MARKETING_PORT=3010
FLUXER_MARKETING_HOST=0.0.0.0
FLUXER_MARKETING_BASE_PATH=/marketing
FLUXER_MARKETING_SECRET_KEY_BASE=dev-marketing-secret-key-base
FLUXER_SUDO_MODE_SECRET=dev-sudo-secret FLUXER_SUDO_MODE_SECRET=dev-sudo-secret
FLUXER_CONNECTION_INITIATION_SECRET=dev-connection-initiation-secret FLUXER_CONNECTION_INITIATION_SECRET=dev-connection-initiation-secret
-1
View File
@@ -64,7 +64,6 @@ function setDefaultTestEnv(): void {
FLUXER_MEDIA_PROXY_SECRET_KEY: 'test-media-secret', FLUXER_MEDIA_PROXY_SECRET_KEY: 'test-media-secret',
FLUXER_ADMIN_SECRET_KEY_BASE: 'test-admin-secret', FLUXER_ADMIN_SECRET_KEY_BASE: 'test-admin-secret',
FLUXER_ADMIN_OAUTH_CLIENT_SECRET: 'test-admin-oauth-secret', FLUXER_ADMIN_OAUTH_CLIENT_SECRET: 'test-admin-oauth-secret',
FLUXER_MARKETING_SECRET_KEY_BASE: 'test-marketing-secret',
FLUXER_APP_PROXY_PORT: '8773', FLUXER_APP_PROXY_PORT: '8773',
FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT: 'http://127.0.0.1:8088/media', FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT: 'http://127.0.0.1:8088/media',
FLUXER_GATEWAY_RPC_AUTH_TOKEN: 'test-gateway-rpc-token', FLUXER_GATEWAY_RPC_AUTH_TOKEN: 'test-gateway-rpc-token',
-9
View File
@@ -149,12 +149,6 @@ function defaultConfig(): MasterConfig {
secret_key_base: '', secret_key_base: '',
oauth_client_secret: '', oauth_client_secret: '',
}, },
marketing: {
port: 3010,
host: '0.0.0.0',
base_path: '/marketing',
secret_key_base: '',
},
app_proxy: { app_proxy: {
port: 8773, port: 8773,
assets_dir: 'fluxer_app/dist', assets_dir: 'fluxer_app/dist',
@@ -486,9 +480,6 @@ function normalizeConfig(config: MasterConfig): MasterConfig {
validateUploadRelaySecret(config.services.media_proxy.upload_relay.secret_base64, config.services.media_proxy.mode); validateUploadRelaySecret(config.services.media_proxy.upload_relay.secret_base64, config.services.media_proxy.mode);
requireString(config.services.admin.secret_key_base, 'FLUXER_ADMIN_SECRET_KEY_BASE'); requireString(config.services.admin.secret_key_base, 'FLUXER_ADMIN_SECRET_KEY_BASE');
requireString(config.services.admin.oauth_client_secret, 'FLUXER_ADMIN_OAUTH_CLIENT_SECRET'); requireString(config.services.admin.oauth_client_secret, 'FLUXER_ADMIN_OAUTH_CLIENT_SECRET');
if (!config.instance.self_hosted) {
requireString(config.services.marketing.secret_key_base, 'FLUXER_MARKETING_SECRET_KEY_BASE');
}
requireString(config.services.gateway.rpc_auth_token, 'FLUXER_GATEWAY_RPC_AUTH_TOKEN'); requireString(config.services.gateway.rpc_auth_token, 'FLUXER_GATEWAY_RPC_AUTH_TOKEN');
return config; return config;
} }
-6
View File
@@ -155,12 +155,6 @@ export interface MasterConfig {
secret_key_base: string; secret_key_base: string;
oauth_client_secret: string; oauth_client_secret: string;
}; };
marketing: {
port: number;
host: string;
base_path: string;
secret_key_base: string;
};
app_proxy: { app_proxy: {
port: number; port: number;
assets_dir: string; assets_dir: string;
@@ -22,7 +22,6 @@ const MINIMAL_ENV: Record<string, string> = {
FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64: 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=', FLUXER_MEDIA_PROXY_UPLOAD_RELAY_SECRET_BASE64: 'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8=',
FLUXER_ADMIN_SECRET_KEY_BASE: 'test-admin-secret', FLUXER_ADMIN_SECRET_KEY_BASE: 'test-admin-secret',
FLUXER_ADMIN_OAUTH_CLIENT_SECRET: 'test-admin-oauth-secret', FLUXER_ADMIN_OAUTH_CLIENT_SECRET: 'test-admin-oauth-secret',
FLUXER_MARKETING_SECRET_KEY_BASE: 'test-marketing-secret',
FLUXER_APP_PROXY_PORT: '8773', FLUXER_APP_PROXY_PORT: '8773',
FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT: 'http://127.0.0.1:8088/media', FLUXER_GATEWAY_MEDIA_PROXY_ENDPOINT: 'http://127.0.0.1:8088/media',
FLUXER_GATEWAY_RPC_AUTH_TOKEN: 'test-gateway-token', FLUXER_GATEWAY_RPC_AUTH_TOKEN: 'test-gateway-token',
@@ -406,23 +405,6 @@ describe('ConfigLoader', () => {
expect(config.database.postgres.ssl).toBe(false); expect(config.database.postgres.ssl).toBe(false);
}); });
test('does not require a marketing secret for self-hosted instances', async () => {
stubMinimalEnv({FLUXER_SELF_HOSTED: 'true'});
vi.stubEnv('FLUXER_MARKETING_SECRET_KEY_BASE', undefined);
const config = await loadConfig();
expect(config.instance.self_hosted).toBe(true);
expect(config.services.marketing.secret_key_base).toBe('');
});
test('requires a marketing secret for hosted instances', async () => {
stubMinimalEnv();
vi.stubEnv('FLUXER_MARKETING_SECRET_KEY_BASE', undefined);
await expect(loadConfig()).rejects.toThrow('FLUXER_MARKETING_SECRET_KEY_BASE');
});
test('still requires TLS for non-self-hosted production Postgres', async () => { test('still requires TLS for non-self-hosted production Postgres', async () => {
stubMinimalEnv({ stubMinimalEnv({
FLUXER_ENV: 'production', FLUXER_ENV: 'production',
@@ -166,10 +166,6 @@ const NAMED_FLUXER_ENV_OVERRIDES: Record<string, NamedEnvOverride> = {
FLUXER_ADMIN_BASE_PATH: {path: ['services', 'admin', 'base_path']}, FLUXER_ADMIN_BASE_PATH: {path: ['services', 'admin', 'base_path']},
FLUXER_ADMIN_SECRET_KEY_BASE: {path: ['services', 'admin', 'secret_key_base']}, FLUXER_ADMIN_SECRET_KEY_BASE: {path: ['services', 'admin', 'secret_key_base']},
FLUXER_ADMIN_OAUTH_CLIENT_SECRET: {path: ['services', 'admin', 'oauth_client_secret']}, FLUXER_ADMIN_OAUTH_CLIENT_SECRET: {path: ['services', 'admin', 'oauth_client_secret']},
FLUXER_MARKETING_HOST: {path: ['services', 'marketing', 'host']},
FLUXER_MARKETING_PORT: {path: ['services', 'marketing', 'port'], parse: parseInteger},
FLUXER_MARKETING_BASE_PATH: {path: ['services', 'marketing', 'base_path']},
FLUXER_MARKETING_SECRET_KEY_BASE: {path: ['services', 'marketing', 'secret_key_base']},
FLUXER_APP_PROXY_PORT: {path: ['services', 'app_proxy', 'port'], parse: parseInteger}, FLUXER_APP_PROXY_PORT: {path: ['services', 'app_proxy', 'port'], parse: parseInteger},
FLUXER_STATIC_DIR: {path: ['services', 'app_proxy', 'assets_dir']}, FLUXER_STATIC_DIR: {path: ['services', 'app_proxy', 'assets_dir']},
FLUXER_GATEWAY_PORT: {path: ['services', 'gateway', 'port'], parse: parseInteger}, FLUXER_GATEWAY_PORT: {path: ['services', 'gateway', 'port'], parse: parseInteger},
@@ -1,99 +0,0 @@
Copyright 2022 The Radio Canada Big Project Authors
(https://github.com/googlefonts/radiocanadadisplay)
Copyright 2022 The Noto Project Authors
(https://github.com/notofonts/latin-greek-cyrillic)
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/arabic)
Copyright 2024 The Noto Project Authors (https://github.com/notofonts/hebrew)
Copyright 2022 The Noto Project Authors (https://github.com/notofonts/thai)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the fonts.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
-7
View File
@@ -1,7 +0,0 @@
# Marketing font licences
The Radio Canada Big and Noto Sans webfont files in this directory are
the unmodified variable-font subsets used by the Fluxer marketing site.
They are distributed under the SIL Open Font License, Version 1.1. The
complete license and the bundled copyright notice are in
`LICENSE-OFL-1.1.txt`.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
-116
View File
@@ -1,116 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
@font-face {
font-family: 'Radio Canada Big';
src: url('RadioCanadaBig-latin.woff2') format('woff2');
font-display: swap;
font-weight: 400 700;
font-style: normal;
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Radio Canada Big';
src: url('RadioCanadaBig-latin-ext.woff2') format('woff2');
font-display: swap;
font-weight: 400 700;
font-style: normal;
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
font-family: 'Noto Sans';
src: url('NotoSans-cyrillic.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
font-style: normal;
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
font-family: 'Noto Sans';
src: url('NotoSans-cyrillic-ext.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
font-style: normal;
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
@font-face {
font-family: 'Noto Sans';
src: url('NotoSans-devanagari.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
font-style: normal;
unicode-range: U+0900-097F, U+1CD0-1CF9, U+200C-200D, U+20A8, U+20B9, U+20F0, U+25CC, U+A830-A839, U+A8E0-A8FF, U+11B00-11B09;
}
@font-face {
font-family: 'Noto Sans';
src: url('NotoSans-greek.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
font-style: normal;
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
@font-face {
font-family: 'Noto Sans';
src: url('NotoSans-greek-ext.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
font-style: normal;
unicode-range: U+1F00-1FFF;
}
@font-face {
font-family: 'Noto Sans';
src: url('NotoSans-vietnamese.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
font-style: normal;
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
@font-face {
font-family: 'Noto Sans Arabic';
src: url('NotoSans-Arabic.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
font-style: normal;
unicode-range: U+0600-06FF, U+0750-077F, U+0870-088E, U+0890-0891, U+0897-08E1, U+08E3-08FF, U+200C-200E, U+2010-2011, U+204F, U+2E41, U+FB50-FDFF, U+FE70-FE74, U+FE76-FEFC, U+102E0-102FB, U+10E60-10E7E, U+10EC2-10EC4, U+10EFC-10EFF, U+1EE00-1EE03, U+1EE05-1EE1F, U+1EE21-1EE22, U+1EE24, U+1EE27, U+1EE29-1EE32, U+1EE34-1EE37, U+1EE39, U+1EE3B, U+1EE42, U+1EE47, U+1EE49, U+1EE4B, U+1EE4D-1EE4F, U+1EE51-1EE52, U+1EE54, U+1EE57, U+1EE59, U+1EE5B, U+1EE5D, U+1EE5F, U+1EE61-1EE62, U+1EE64, U+1EE67-1EE6A, U+1EE6C-1EE72, U+1EE74-1EE77, U+1EE79-1EE7C, U+1EE7E, U+1EE80-1EE89, U+1EE8B-1EE9B, U+1EEA1-1EEA3, U+1EEA5-1EEA9, U+1EEAB-1EEBB, U+1EEF0-1EEF1;
}
@font-face {
font-family: 'Noto Sans Hebrew';
src: url('NotoSans-Hebrew.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
font-style: normal;
unicode-range: U+0307-0308, U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
}
@font-face {
font-family: 'Noto Sans Thai';
src: url('NotoSans-Thai.woff2') format('woff2');
font-display: swap;
font-weight: 100 900;
font-style: normal;
unicode-range: U+02D7, U+0303, U+0331, U+0E01-0E5B, U+200C-200D, U+25CC;
}
:root {
--fluxer-font-stack: 'Radio Canada Big', 'Noto Sans', system-ui, sans-serif;
}
:root:lang(ar) {
--fluxer-font-stack: 'Radio Canada Big', 'Noto Sans Arabic', 'Noto Sans', system-ui, sans-serif;
}
:root:lang(he) {
--fluxer-font-stack: 'Radio Canada Big', 'Noto Sans Hebrew', 'Noto Sans', system-ui, sans-serif;
}
:root:lang(th) {
--fluxer-font-stack: 'Radio Canada Big', 'Noto Sans Thai', 'Noto Sans', system-ui, sans-serif;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: AGPL-3.0-or-later
set -euo pipefail
repo_root="$(git rev-parse --show-toplevel)"
cd "$repo_root"
initialize_command="git -c submodule.fluxer_marketing.update=checkout submodule update --init -- fluxer_marketing"
if ! git -c submodule.fluxer_marketing.update=checkout submodule update --init -- fluxer_marketing; then
printf '%s\n' \
"Unable to initialize the private fluxerapp/marketing repository." \
"Confirm that your GitHub account has access and that your existing Git credentials can authenticate, then run:" \
" $initialize_command" >&2
exit 1
fi
if [[ ! -f fluxer_marketing/Cargo.toml ]]; then
printf '%s\n' \
"The marketing submodule initialized without its required Cargo.toml." \
"Remove the inconsistent checkout and run:" \
" $initialize_command" >&2
exit 1
fi
printf '%s\n' \
"Private marketing source is initialized at fluxer_marketing." \
"Install its dependencies explicitly with:" \
" pnpm --dir fluxer_marketing install --frozen-lockfile"
-4
View File
@@ -522,10 +522,6 @@ fn validate_component(component: &str) -> Result<()> {
}), }),
"Invalid release component {component:?}: expected lowercase letters, digits, and single hyphen separators" "Invalid release component {component:?}: expected lowercase letters, digits, and single hyphen separators"
); );
ensure!(
component != "fluxer-marketing" && component != "marketing",
"Marketing must not publish a public GitHub Release"
);
Ok(()) Ok(())
} }
+2 -239
View File
@@ -31,7 +31,6 @@ const DEFAULT_TASKS: &[&str] = &[
"proxy", "proxy",
"services", "services",
"media", "media",
"marketing",
"admin", "admin",
"api", "api",
"gateway-single", "gateway-single",
@@ -45,32 +44,12 @@ const OBJECT_STORE_TASKS: &[&str] = &["api", "worker", "media"];
const CLOUDFLARE_TUNNEL_TASK: &str = "cloudflare-tunnel"; const CLOUDFLARE_TUNNEL_TASK: &str = "cloudflare-tunnel";
const OBJECT_STORE_STARTUP_REPAIR_TIMEOUT_SECS: u64 = 60; const OBJECT_STORE_STARTUP_REPAIR_TIMEOUT_SECS: u64 = 60;
const DEFAULT_OBJECT_STORE_MONITOR_INTERVAL_SECS: u64 = 15; const DEFAULT_OBJECT_STORE_MONITOR_INTERVAL_SECS: u64 = 15;
const MARKETING_MANIFEST: &str = "fluxer_marketing/Cargo.toml";
const MARKETING_INITIALIZE_COMMAND: &str =
"git -c submodule.fluxer_marketing.update=checkout submodule update --init -- fluxer_marketing";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum MarketingAvailability {
Unavailable,
Available,
}
pub async fn run_dev(task_names: &[String], cloudflare_tunnel: bool) -> Result<i32> { pub async fn run_dev(task_names: &[String], cloudflare_tunnel: bool) -> Result<i32> {
let marketing_availability = marketing_availability()?; let mut tasks = task_table()?;
if task_names.iter().any(|name| name == "marketing")
&& marketing_availability == MarketingAvailability::Unavailable
{
bail!(
"The private marketing project is unavailable. Authorized maintainers can initialize it with:\n {MARKETING_INITIALIZE_COMMAND}"
);
}
let mut tasks = task_table_for_availability(marketing_availability)?;
let selected = if task_names.is_empty() { let selected = if task_names.is_empty() {
DEFAULT_TASKS DEFAULT_TASKS
.iter() .iter()
.filter(|name| {
**name != "marketing" || marketing_availability == MarketingAvailability::Available
})
.map(|name| (*name).to_owned()) .map(|name| (*name).to_owned())
.collect::<Vec<_>>() .collect::<Vec<_>>()
} else { } else {
@@ -344,20 +323,12 @@ fn normalize_selected_tasks(mut selected: Vec<String>, cloudflare_tunnel: bool)
} }
pub fn task_table() -> Result<BTreeMap<&'static str, DevTask>> { pub fn task_table() -> Result<BTreeMap<&'static str, DevTask>> {
task_table_for_availability(marketing_availability()?)
}
fn task_table_for_availability(
marketing_availability: MarketingAvailability,
) -> Result<BTreeMap<&'static str, DevTask>> {
let self_tool = self_tool_command()?; let self_tool = self_tool_command()?;
let api_dir = ROOT.join("fluxer_api"); let api_dir = ROOT.join("fluxer_api");
let api_tsx = api_dir.join("node_modules/.bin/tsx"); let api_tsx = api_dir.join("node_modules/.bin/tsx");
let app_dir = ROOT.join("fluxer_app"); let app_dir = ROOT.join("fluxer_app");
let app_dev_server = ROOT.join("tools/ci/run.sh"); let app_dev_server = ROOT.join("tools/ci/run.sh");
let public_url = public_url(); let public_url = public_url();
let marketing_endpoint = std::env::var("FLUXER_MARKETING_ENDPOINT")
.unwrap_or_else(|_| format!("{public_url}/marketing"));
let admin_endpoint = let admin_endpoint =
std::env::var("FLUXER_ADMIN_ENDPOINT").unwrap_or_else(|_| format!("{public_url}/admin")); std::env::var("FLUXER_ADMIN_ENDPOINT").unwrap_or_else(|_| format!("{public_url}/admin"));
let mut tasks = BTreeMap::new(); let mut tasks = BTreeMap::new();
@@ -515,70 +486,6 @@ fn task_table_for_availability(
Some("true".to_owned()), Some("true".to_owned()),
)], )],
}); });
if marketing_availability == MarketingAvailability::Available {
insert(DevTask {
name: "marketing",
args: strings(&[
"cargo",
"watch",
"--no-dot-ignores",
"-w",
"fluxer_marketing/src",
"-w",
MARKETING_MANIFEST,
"-w",
"fluxer_marketing/build.rs",
"-w",
"fluxer_marketing/content",
"-w",
"fluxer_marketing/Cargo.lock",
"-w",
"fluxer_marketing/package.json",
"-w",
"fluxer_marketing/pnpm-lock.yaml",
"-w",
"fluxer_marketing/pnpm-workspace.yaml",
"-w",
"fluxer_marketing/static",
"-w",
"Cargo.toml",
"-w",
"fluxer_common",
"-w",
"packages/fonts/manifest.json",
"-w",
"packages/fonts/NOTICE.md",
"-w",
"packages/fonts/LICENSE-IBM-PLEX.txt",
"-w",
"packages/fonts/css/locale-fallbacks.css",
"-w",
"packages/fonts/files/FluxerSans",
"-w",
"packages/fonts/files/FluxerMono",
"-w",
"packages/i18n/marketing",
"-x",
"run --manifest-path fluxer_marketing/Cargo.toml -p fluxer_marketing",
]),
cwd: ROOT.clone(),
env: vec![
("FLUXER_APP_ENDPOINT".to_owned(), Some(public_url.clone())),
(
"FLUXER_MARKETING_BASE_PATH".to_owned(),
Some("/marketing".to_owned()),
),
(
"FLUXER_MARKETING_ENDPOINT".to_owned(),
Some(marketing_endpoint),
),
(
"FLUXER_STATIC_CDN_ENDPOINT".to_owned(),
Some(public_url.clone()),
),
],
});
}
insert(DevTask { insert(DevTask {
name: "admin", name: "admin",
args: strings(&[ args: strings(&[
@@ -628,74 +535,6 @@ fn task_table_for_availability(
Ok(tasks) Ok(tasks)
} }
fn marketing_availability() -> Result<MarketingAvailability> {
let marketing_path = ROOT.join("fluxer_marketing");
let manifest_path = ROOT.join(MARKETING_MANIFEST);
if manifest_path.is_file() {
ensure_marketing_manifest(&manifest_path)?;
return Ok(MarketingAvailability::Available);
}
if marketing_path.exists() {
let mut entries = std::fs::read_dir(&marketing_path)
.with_context(|| format!("failed to inspect {}", marketing_path.display()))?;
if entries.next().transpose()?.is_some() {
bail!(
"The marketing path {} exists but does not contain the required Cargo.toml; remove the inconsistent checkout and run:\n {MARKETING_INITIALIZE_COMMAND}",
marketing_path.display()
);
}
}
Ok(MarketingAvailability::Unavailable)
}
fn ensure_marketing_manifest(path: &Path) -> Result<()> {
let output = Command::new("cargo")
.args([
"metadata",
"--manifest-path",
MARKETING_MANIFEST,
"--locked",
"--offline",
"--no-deps",
"--format-version",
"1",
])
.current_dir(ROOT.as_path())
.output()
.with_context(|| format!("failed to validate {}", path.display()))?;
if !output.status.success() {
bail!(
"The initialized private marketing manifest {} is malformed or inconsistent: {}",
path.display(),
String::from_utf8_lossy(&output.stderr).trim()
);
}
let metadata: serde_json::Value = serde_json::from_slice(&output.stdout)
.with_context(|| format!("Cargo emitted invalid metadata for {}", path.display()))?;
let packages = metadata["packages"]
.as_array()
.with_context(|| format!("Cargo metadata for {} is missing packages", path.display()))?;
let expected_manifest_path = path
.canonicalize()
.with_context(|| format!("failed to resolve {}", path.display()))?;
let valid_package = packages.iter().any(|package| {
package["name"].as_str() == Some("fluxer_marketing")
&& package["manifest_path"]
.as_str()
.map(Path::new)
.and_then(|manifest_path| manifest_path.canonicalize().ok())
.as_deref()
== Some(expected_manifest_path.as_path())
});
if !valid_package {
bail!(
"The initialized private marketing manifest {} does not define the root package fluxer_marketing",
path.display()
);
}
Ok(())
}
async fn wait_for_search_backend_if_needed(selected: &[String]) -> Result<()> { async fn wait_for_search_backend_if_needed(selected: &[String]) -> Result<()> {
if !selected if !selected
.iter() .iter()
@@ -781,26 +620,6 @@ async fn ensure_js_dependencies_if_needed(
AwaitOutcome::Shutdown(signal) => return Ok(AwaitOutcome::Shutdown(signal)), AwaitOutcome::Shutdown(signal) => return Ok(AwaitOutcome::Shutdown(signal)),
} }
} }
if selected.iter().any(|name| name == "marketing") {
let marketing_dir = ROOT.join("fluxer_marketing");
match run_command_interruptible(
&["pnpm", "install", "--frozen-lockfile"],
RunOptions {
cwd: &marketing_dir,
env: PNPM_INSTALL_ENV
.iter()
.map(|(key, value)| ((*key).to_owned(), Some((*value).to_owned())))
.collect(),
..RunOptions::default()
},
shutdown,
)
.await?
{
AwaitOutcome::Completed(_) => {}
AwaitOutcome::Shutdown(signal) => return Ok(AwaitOutcome::Shutdown(signal)),
}
}
Ok(AwaitOutcome::Completed(())) Ok(AwaitOutcome::Completed(()))
} }
@@ -950,15 +769,6 @@ async fn wait_for_cloudflare_tunnel_routes(
) )
.await?; .await?;
} }
if has_task("marketing") {
wait_http_for_dev_tasks(
processes,
"dev proxy marketing",
&format!("{base_url}/marketing/"),
timeout,
)
.await?;
}
if has_task("admin") { if has_task("admin") {
wait_http_for_dev_tasks( wait_http_for_dev_tasks(
processes, processes,
@@ -1096,7 +906,6 @@ mod tests {
"proxy", "proxy",
"services", "services",
"media", "media",
"marketing",
"admin", "admin",
"api", "api",
"gateway-single", "gateway-single",
@@ -1171,7 +980,7 @@ mod tests {
#[test] #[test]
fn task_table_contains_recursive_binary_tasks() { fn task_table_contains_recursive_binary_tasks() {
let tasks = task_table_for_availability(MarketingAvailability::Available).unwrap(); let tasks = task_table().unwrap();
let expected_public_url = public_url(); let expected_public_url = public_url();
let expected_app_port = APP_PORT.to_string(); let expected_app_port = APP_PORT.to_string();
let expected_admin_port = ADMIN_PORT.to_string(); let expected_admin_port = ADMIN_PORT.to_string();
@@ -1287,52 +1096,6 @@ mod tests {
"-p".to_owned(), "-p".to_owned(),
"fluxer-media-proxy".to_owned() "fluxer-media-proxy".to_owned()
])); ]));
assert_eq!(
tasks["marketing"].args,
vec![
"cargo".to_owned(),
"watch".to_owned(),
"--no-dot-ignores".to_owned(),
"-w".to_owned(),
"fluxer_marketing/src".to_owned(),
"-w".to_owned(),
"fluxer_marketing/Cargo.toml".to_owned(),
"-w".to_owned(),
"fluxer_marketing/build.rs".to_owned(),
"-w".to_owned(),
"fluxer_marketing/content".to_owned(),
"-w".to_owned(),
"fluxer_marketing/Cargo.lock".to_owned(),
"-w".to_owned(),
"fluxer_marketing/package.json".to_owned(),
"-w".to_owned(),
"fluxer_marketing/pnpm-lock.yaml".to_owned(),
"-w".to_owned(),
"fluxer_marketing/pnpm-workspace.yaml".to_owned(),
"-w".to_owned(),
"fluxer_marketing/static".to_owned(),
"-w".to_owned(),
"Cargo.toml".to_owned(),
"-w".to_owned(),
"fluxer_common".to_owned(),
"-w".to_owned(),
"packages/fonts/manifest.json".to_owned(),
"-w".to_owned(),
"packages/fonts/NOTICE.md".to_owned(),
"-w".to_owned(),
"packages/fonts/LICENSE-IBM-PLEX.txt".to_owned(),
"-w".to_owned(),
"packages/fonts/css/locale-fallbacks.css".to_owned(),
"-w".to_owned(),
"packages/fonts/files/FluxerSans".to_owned(),
"-w".to_owned(),
"packages/fonts/files/FluxerMono".to_owned(),
"-w".to_owned(),
"packages/i18n/marketing".to_owned(),
"-x".to_owned(),
"run --manifest-path fluxer_marketing/Cargo.toml -p fluxer_marketing".to_owned()
]
);
assert_eq!( assert_eq!(
tasks["admin"].args, tasks["admin"].args,
vec![ vec![
-8
View File
@@ -15,7 +15,6 @@ pub const API_PORT: u16 = 8080;
pub const GATEWAY_PORT: u16 = 8771; pub const GATEWAY_PORT: u16 = 8771;
pub const GATEWAY_WEBSOCKET_PORTS: &[u16] = &[8771, 8772, 8774]; pub const GATEWAY_WEBSOCKET_PORTS: &[u16] = &[8771, 8772, 8774];
pub const MEDIA_PROXY_PORT: u16 = 8082; pub const MEDIA_PROXY_PORT: u16 = 8082;
pub const MARKETING_PORT: u16 = 3010;
pub const LIVEKIT_PORT: u16 = 7880; pub const LIVEKIT_PORT: u16 = 7880;
pub const DEVMAIL_PORT: u16 = 8025; pub const DEVMAIL_PORT: u16 = 8025;
@@ -108,13 +107,6 @@ pub const PROXY_ROUTES: &[ProxyRoute] = &[
strip_prefix: false, strip_prefix: false,
alternate_ports: &[], alternate_ports: &[],
}, },
ProxyRoute {
prefix: "/marketing",
host: LOOPBACK_HOST,
port: MARKETING_PORT,
strip_prefix: true,
alternate_ports: &[],
},
ProxyRoute { ProxyRoute {
prefix: "/assets", prefix: "/assets",
host: LOOPBACK_HOST, host: LOOPBACK_HOST,
+2 -22
View File
@@ -77,8 +77,6 @@ pub enum CatalogLayout {
pub enum CatalogName { pub enum CatalogName {
#[value(name = "app")] #[value(name = "app")]
App, App,
#[value(name = "marketing")]
Marketing,
#[value(name = "errors")] #[value(name = "errors")]
Errors, Errors,
#[value(name = "api-content", alias = "content")] #[value(name = "api-content", alias = "content")]
@@ -91,7 +89,6 @@ impl CatalogName {
fn label(self) -> &'static str { fn label(self) -> &'static str {
match self { match self {
CatalogName::App => "app", CatalogName::App => "app",
CatalogName::Marketing => "marketing",
CatalogName::Errors => "errors", CatalogName::Errors => "errors",
CatalogName::ApiContent => "api-content", CatalogName::ApiContent => "api-content",
CatalogName::Email => "email", CatalogName::Email => "email",
@@ -123,10 +120,6 @@ impl RuntimeConfig {
.unwrap_or_else(|| PathBuf::from(".")); .unwrap_or_else(|| PathBuf::from("."));
let (locales_dir, catalog_layout) = match catalog { let (locales_dir, catalog_layout) = match catalog {
CatalogName::App => (locales_dir(&app_dir), CatalogLayout::NestedMessages), CatalogName::App => (locales_dir(&app_dir), CatalogLayout::NestedMessages),
CatalogName::Marketing => (
repo_root.join("packages/i18n/marketing"),
CatalogLayout::FlatPo,
),
CatalogName::Errors => ( CatalogName::Errors => (
repo_root repo_root
.join("packages") .join("packages")
@@ -277,14 +270,9 @@ struct RawTranslateArgs {
#[arg( #[arg(
long, long,
value_enum, value_enum,
help = "Catalog target to translate: app, marketing, errors, api-content, or email" help = "Catalog target to translate: app, errors, api-content, or email"
)] )]
catalog: Option<CatalogName>, catalog: Option<CatalogName>,
#[arg(
long,
help = "Deprecated alias for --catalog marketing; use packages/i18n/marketing flat gettext catalogs"
)]
marketing: bool,
#[arg( #[arg(
long = "msgctxt", long = "msgctxt",
help = "Only translate entries with this PO msgctxt or static catalog key; repeatable. Email fields use <template>.<subject|body>." help = "Only translate entries with this PO msgctxt or static catalog key; repeatable. Email fields use <template>.<subject|body>."
@@ -368,15 +356,7 @@ fn normalize_args(raw: RawTranslateArgs, env_overrides: &EnvOverlay) -> Result<T
reset = true; reset = true;
all = true; all = true;
} }
let catalog = match (raw.catalog, raw.marketing) { let catalog = raw.catalog.unwrap_or(CatalogName::App);
(Some(CatalogName::Marketing), true) | (None, true) => CatalogName::Marketing,
(Some(catalog), false) => catalog,
(None, false) => CatalogName::App,
(Some(catalog), true) => bail!(
"--marketing cannot be combined with --catalog {}; use --catalog marketing",
catalog.label()
),
};
if !all && raw.locales.is_empty() { if !all && raw.locales.is_empty() {
all = true; all = true;
} }