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
5 changes: 5 additions & 0 deletions .changeset/desktop-device-display-name.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
default: patch
---

Settings device name now clearly shows your device type — e.g. "Sable Desktop on Windows", "Sable Mobile on Android", or "Sable Web on Firefox for macOS"
8 changes: 4 additions & 4 deletions .github/actions/prepare-tofu/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ name: Prepare OpenTofu Deployment
description: Prepares OpenTofu in infra/web.

inputs:
is_release_tag:
description: Whether the build is for a release tag. Passed through to VITE_IS_RELEASE_TAG.
build-flavor:
description: Build flavor (stable or dev). Controls app build behavior.
required: false
default: 'false'
default: stable

runs:
using: composite
Expand All @@ -15,7 +15,7 @@ runs:
with:
build: 'true'
env:
VITE_IS_RELEASE_TAG: ${{ inputs.is_release_tag }}
SABLE_BUILD_FLAVOR: ${{ inputs.build-flavor }}
VITE_SENTRY_DSN: ${{ env.VITE_SENTRY_DSN }}
VITE_SENTRY_ENVIRONMENT: ${{ env.VITE_SENTRY_ENVIRONMENT }}
VITE_APP_VERSION: ${{ env.VITE_APP_VERSION }}
Expand Down
41 changes: 41 additions & 0 deletions .github/actions/resolve-release-meta/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Resolve release meta
description: Detect whether the current build is a nightly/dev or stable release and output tag, version, ref, and build flavor.

inputs:
input-tag:
description: Explicit tag for workflow_dispatch triggers
required: false
default: ''

outputs:
tag:
description: Release tag name (e.g. "nightly" or "v1.2.3")
value: ${{ steps.meta.outputs.tag }}
version:
description: SemVer version string
value: ${{ steps.meta.outputs.version }}
ref:
description: Git ref (sha for nightly, tag ref for stable)
value: ${{ steps.meta.outputs.ref }}
nightly:
description: '"true" if this is a nightly/dev build'
value: ${{ steps.meta.outputs.nightly }}
started_at:
description: ISO 8601 timestamp of when the build started
value: ${{ steps.meta.outputs.started_at }}
build_flavor:
description: '"dev" or "stable"'
value: ${{ steps.meta.outputs.build_flavor }}

runs:
using: composite
steps:
- shell: bash
id: meta
env:
EVENT_NAME: ${{ github.event_name }}
INPUT_TAG: ${{ inputs.input-tag }}
GIT_REF: ${{ github.ref }}
GIT_REF_NAME: ${{ github.ref_name }}
GIT_SHA: ${{ github.sha }}
run: node ${{ github.action_path }}/../../scripts/release-meta.mjs "$GITHUB_OUTPUT"
11 changes: 10 additions & 1 deletion .github/scripts/build-updater-manifest.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { execSync } from 'node:child_process';
import { readFileSync, readdirSync, writeFileSync, mkdtempSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { resolveReleaseMeta } from './release-meta.mjs';

const TAG = process.env.TAG;
const VERSION = process.env.VERSION;
Expand All @@ -20,6 +21,14 @@ if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(VERSION)) {
}
const version = VERSION;

const { isNightly } = resolveReleaseMeta({
eventName: process.env.EVENT_NAME,
inputTag: process.env.INPUT_TAG,
gitRef: process.env.GIT_REF,
gitRefName: process.env.GIT_REF_NAME,
gitSha: process.env.GIT_SHA,
});

const dir = mkdtempSync(join(tmpdir(), 'sable-sigs-'));
execSync(`gh release download "${TAG}" --repo "${REPO}" --pattern '*.sig' --dir "${dir}"`, {
stdio: 'inherit',
Expand Down Expand Up @@ -55,7 +64,7 @@ if (Object.keys(platforms).length === 0) {
}

let notes = `Sable ${version}`;
if (TAG !== 'nightly') {
if (!isNightly) {
try {
notes =
execSync(`gh release view "${TAG}" --repo "${REPO}" --json body -q .body`, {
Expand Down
40 changes: 0 additions & 40 deletions .github/scripts/nightly-version.mjs

This file was deleted.

118 changes: 118 additions & 0 deletions .github/scripts/release-meta.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#!/usr/bin/env node
/* oxlint-disable no-console */

import { execSync } from 'node:child_process';
import { readFileSync } from 'node:fs';

export function computeNightlyVersion(baseVersion, timestamp = new Date()) {
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(baseVersion);
if (!match) {
throw new Error(`Version must be stable SemVer (got: ${baseVersion})`);
}

const y = String(timestamp.getUTCFullYear()).slice(-2);
const m = String(timestamp.getUTCMonth() + 1).padStart(2, '0');
const d = String(timestamp.getUTCDate()).padStart(2, '0');
const h = String(timestamp.getUTCHours()).padStart(2, '0');
const min = String(timestamp.getUTCMinutes()).padStart(2, '0');

const [, major, minor, patch] = match;
return `${major}.${minor}.${Number(patch) + 1}-nightly.${y}${m}${d}${h}${min}`;
}

export function nightlyVersion() {
const { version } = JSON.parse(readFileSync('package.json', 'utf8'));

let timestamp;
try {
const commitTimestamp = execSync('git log -1 --format=%ct', {
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'ignore'],
}).trim();
if (/^\d+$/.test(commitTimestamp)) {
timestamp = new Date(Number(commitTimestamp) * 1000);
}
} catch {}

return computeNightlyVersion(version, timestamp);
}

export async function writeOutputs(outputPath, entries) {
const { appendFileSync } = await import('node:fs');
appendFileSync(outputPath, entries.map((e) => `${e.key}=${e.value}`).join('\n') + '\n');
}

export function resolveReleaseMeta({
eventName = '',
inputTag = '',
gitRef = '',
gitRefName = '',
gitSha = '',
} = {}) {
const startedAt = new Date().toISOString();
let tag, ref, isNightly, version;

if (eventName === 'workflow_dispatch' && inputTag) {
tag = inputTag;
ref = inputTag;
isNightly = false;
version = tag.replace(/^v/, '');
} else if (gitRef === 'refs/heads/dev') {
tag = 'nightly';
ref = gitSha;
isNightly = true;
version = nightlyVersion();
} else if (/^v\d+\./.test(gitRefName)) {
tag = gitRefName;
ref = gitRef;
isNightly = false;
version = gitRefName.replace(/^v/, '');
} else {
tag = '';
ref = gitRef;
isNightly = false;
version = '';
}

return {
tag,
version,
ref,
isNightly,
startedAt,
buildFlavor: isNightly ? 'dev' : 'stable',
};
}

const isMain =
process.argv[1] && import.meta.url === `file:///${process.argv[1].replace(/\\/g, '/')}`;
if (isMain) {
try {
const meta = resolveReleaseMeta({
eventName: process.env.EVENT_NAME ?? '',
inputTag: process.env.INPUT_TAG ?? '',
gitRef: process.env.GIT_REF ?? '',
gitRefName: process.env.GIT_REF_NAME ?? '',
gitSha: process.env.GIT_SHA ?? '',
});

const outputPath = process.argv[2];
const entries = [
{ key: 'tag', value: meta.tag },
{ key: 'version', value: meta.version },
{ key: 'ref', value: meta.ref },
{ key: 'nightly', value: String(meta.isNightly) },
{ key: 'started_at', value: meta.startedAt },
{ key: 'build_flavor', value: meta.buildFlavor },
];

if (outputPath) {
await writeOutputs(outputPath, entries);
} else {
entries.forEach(({ key, value }) => console.log(`${key}=${value}`));
}
} catch (err) {
console.error(err.message);
process.exit(1);
}
}
134 changes: 0 additions & 134 deletions .github/workflows/cloudflare-web-deploy.yml

This file was deleted.

Loading
Loading