1b4399df68
Renames the Capacitor app identifier from pub.agora.app to spot.agora.app and cleans up Ditto-branded artifacts that don't refer to upstream Ditto-the-project or Ditto-stack services. App identifier (pub.agora.app -> spot.agora.app): - capacitor.config.ts appId - android applicationId, namespace, package_name string, custom_url_scheme - iOS PRODUCT_BUNDLE_IDENTIFIER (Debug + Release) - public/.well-known/assetlinks.json package_name - public/.well-known/apple-app-site-association app id - Info.plist BGTaskSchedulerPermittedIdentifiers and the matching Swift bgTaskIdentifier (previously mismatched: plist said pub.agora.app.notification-refresh, Swift said pub.ditto.app.notification-refresh, so background refresh would silently fail to register) - src/lib/helpContent.ts Zapstore URLs - .gitlab-ci.yml --package_name for fastlane supply Android Java package (pub.ditto.app -> spot.agora.app): - Move android/app/src/main/java/pub/ditto/app/ -> android/app/src/main/java/spot/agora/app/ (4 files: MainActivity, DittoNotificationPlugin, NostrPoller, NotificationRelayService) - Update package declarations to match the new Android namespace (was a hard build failure with namespace = spot.agora.app) - Update proguard -keep rule - Update NotificationRelayService ACTION_FETCH intent string pub.ditto.app.ACTION_FETCH -> spot.agora.app.ACTION_FETCH Fastlane (pub.ditto.app -> spot.agora.app): - Appfile, Matchfile, Fastfile provisioning profile specifiers. Matchfile still points at Soapbox's certificates git repo; a new match repo with certs for spot.agora.app is required before iOS CI signing works. IPA artifact name (Ditto.ipa -> Agora.ipa): - Fastfile output_name and matching CI artifact paths - .gitlab-ci.yml: artifacts/Ditto.ipa references and the GitLab Generic Packages path from /packages/generic/ditto/ -> /packages/generic/agora/ (matches how APK/AAB are already published). Existing release artifacts at the old path remain reachable; new releases land at the new path. Release-notes script fallback (Ditto vX.Y.Z -> Agora vX.Y.Z): - scripts/extract-release-notes.mjs fallback used as the App Store / Play Store 'What's New' blurb when a changelog section has no summary. manifest.webmanifest: - Update related_applications Play Store entry to spot.agora.app. - Remove the iTunes related_applications entry that pointed at the existing Ditto App Store listing; not applicable to Agora until Agora has its own listing. Capacitor sync incidentals: - npm run cap:sync picked up @capacitor/barcode-scanner registration that had been missed in a prior plugin install (android/app/capacitor.build.gradle, capacitor.settings.gradle, ios/App/CapApp-SPM/Package.swift). Intentionally NOT touched: - ditto.json filename, DittoConfigSchema, DittoConfig, and JSDoc references to ditto.json. The config-system shape is shared with upstream Ditto by design. - relay.ditto.pub, blossom.ditto.pub, ditto.pub/api/* and other Ditto-stack services Agora actively consumes. - The DittoNotificationPlugin Android/iOS class name, the DittoNotification JS bridge name, ditto_notification_config SharedPreferences keys, ic_stat_ditto drawables, and the DittoBridgeViewController. Renaming requires a coordinated JS-side rename plus a SharedPreferences migration or existing users on the Ditto fork lose their notification config on upgrade. - Ditto references in skill docs, NIP.md kind comments, README, and zapstore.yaml attribution \u2014 those correctly describe the upstream Ditto project that Agora forked from. Follow-ups required before CI succeeds end-to-end (out of scope here): - Stand up a new fastlane match git repo containing certs + provisioning profiles for spot.agora.app, or update Matchfile git_url to point at it. - Register spot.agora.app in App Store Connect for team GZLTTH5DLM and create a new App Store listing. - Create a new Google Play Console listing for spot.agora.app (package name is immutable per app on Play; the existing pub.agora.app listing cannot be reused). - Re-publish to Zapstore under spot.agora.app so the URLs in helpContent.ts resolve.
142 lines
4.3 KiB
JavaScript
Executable File
142 lines
4.3 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* Extract release notes from CHANGELOG.md for a given version.
|
|
*
|
|
* The CHANGELOG follows Keep a Changelog format with one extension: each release
|
|
* section MAY begin with a single plaintext paragraph (the "summary") before any
|
|
* `### Added` / `### Changed` / etc. heading. The summary is used as the release
|
|
* blurb on the App Store, Play Store, and the in-app version-update toast. The
|
|
* full section body is used as the GitLab Release description.
|
|
*
|
|
* Format:
|
|
*
|
|
* ## [X.Y.Z] - YYYY-MM-DD
|
|
*
|
|
* A short single-paragraph summary (max 500 characters by convention).
|
|
*
|
|
* ### Added
|
|
* - bullet
|
|
* - bullet
|
|
*
|
|
* ### Changed
|
|
* - bullet
|
|
*
|
|
* Usage:
|
|
* node scripts/extract-release-notes.mjs <version> [--summary] [--changelog <path>]
|
|
*
|
|
* --summary Print only the summary paragraph (no headings, no bullets).
|
|
* Falls back to "Agora vX.Y.Z" if the section has no summary.
|
|
* --changelog Path to the changelog file. Defaults to CHANGELOG.md.
|
|
*
|
|
* Exits 0 with the extracted text on stdout. Exits non-zero if the version is
|
|
* not found in the changelog.
|
|
*/
|
|
|
|
import { readFileSync } from 'node:fs';
|
|
import { argv, exit, stderr, stdout } from 'node:process';
|
|
|
|
function parseArgs(args) {
|
|
let version;
|
|
let summary = false;
|
|
let changelog = 'CHANGELOG.md';
|
|
for (let i = 0; i < args.length; i++) {
|
|
const arg = args[i];
|
|
if (arg === '--summary') summary = true;
|
|
else if (arg === '--changelog') changelog = args[++i];
|
|
else if (!arg.startsWith('--') && !version) version = arg;
|
|
else {
|
|
stderr.write(`Unknown argument: ${arg}\n`);
|
|
exit(2);
|
|
}
|
|
}
|
|
if (!version) {
|
|
stderr.write('Usage: extract-release-notes.mjs <version> [--summary] [--changelog <path>]\n');
|
|
exit(2);
|
|
}
|
|
// Strip a leading "v" so callers can pass either "v2.14.3" or "2.14.3".
|
|
if (version.startsWith('v')) version = version.slice(1);
|
|
return { version, summary, changelog };
|
|
}
|
|
|
|
/**
|
|
* Extract the lines belonging to a single version section from changelog text,
|
|
* not including the version heading itself.
|
|
*/
|
|
function extractSection(markdown, version) {
|
|
const lines = markdown.split('\n');
|
|
const headingPattern = new RegExp(
|
|
`^## \\[${version.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\$&')}\\]`,
|
|
);
|
|
const nextHeadingPattern = /^## \[/;
|
|
let inSection = false;
|
|
const out = [];
|
|
for (const line of lines) {
|
|
if (!inSection) {
|
|
if (headingPattern.test(line)) {
|
|
inSection = true;
|
|
continue;
|
|
}
|
|
} else {
|
|
if (nextHeadingPattern.test(line)) break;
|
|
out.push(line);
|
|
}
|
|
}
|
|
return inSection ? out : null;
|
|
}
|
|
|
|
/**
|
|
* Pull the leading non-blank paragraph from a section, stopping at the first
|
|
* `###` category heading or `-` bullet. Returns null if no summary paragraph.
|
|
*/
|
|
function extractSummary(sectionLines) {
|
|
const paragraph = [];
|
|
let started = false;
|
|
for (const line of sectionLines) {
|
|
const trimmed = line.trim();
|
|
if (!started) {
|
|
if (!trimmed) continue;
|
|
// If the very first non-blank line is a heading or bullet, there's no summary.
|
|
if (trimmed.startsWith('#') || trimmed.startsWith('- ')) return null;
|
|
started = true;
|
|
paragraph.push(trimmed);
|
|
continue;
|
|
}
|
|
// We're inside the paragraph. A blank line, a heading, or a bullet ends it.
|
|
if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('- ')) break;
|
|
paragraph.push(trimmed);
|
|
}
|
|
return paragraph.length ? paragraph.join(' ') : null;
|
|
}
|
|
|
|
/** Trim leading and trailing blank lines from a list of lines. */
|
|
function trimBlankEdges(lines) {
|
|
let start = 0;
|
|
let end = lines.length;
|
|
while (start < end && !lines[start].trim()) start++;
|
|
while (end > start && !lines[end - 1].trim()) end--;
|
|
return lines.slice(start, end);
|
|
}
|
|
|
|
const { version, summary, changelog } = parseArgs(argv.slice(2));
|
|
const markdown = readFileSync(changelog, 'utf8');
|
|
const section = extractSection(markdown, version);
|
|
|
|
if (!section) {
|
|
stderr.write(`Version ${version} not found in ${changelog}\n`);
|
|
exit(1);
|
|
}
|
|
|
|
if (summary) {
|
|
const text = extractSummary(section);
|
|
stdout.write(text ?? `Agora v${version}`);
|
|
stdout.write('\n');
|
|
} else {
|
|
const body = trimBlankEdges(section).join('\n');
|
|
if (body) {
|
|
stdout.write(body);
|
|
stdout.write('\n');
|
|
} else {
|
|
stdout.write(`Agora v${version}\n`);
|
|
}
|
|
}
|