Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .github/workflows/publish-aur.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ jobs:

- name: Checkout packaging sources
uses: actions/checkout@v6
with:
sparse-checkout: packaging

- name: Create unprivileged build user
run: |
Expand Down
137 changes: 81 additions & 56 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,58 @@ jobs:
resource_key: win32-arm64
cli_archive: true

# Everything after the desktop builds runs one job after another, so their
# Node inputs are built here, alongside those builds, and those jobs skip
# checkout and install.
prepare_publish:
name: Prepare publish tools
needs: [preflight]
if: ${{ !cancelled() && needs.preflight.result == 'success' }}
runs-on: blacksmith-2vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.ref }}
sparse-checkout: |
/*
!/.repos/
sparse-checkout-cone-mode: false

- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
with:
node-version-file: package.json
cache: true
run-install: |
args:
- --filter=@t3tools/scripts...

- name: Build the npm launcher
run: node scripts/build-npm-launcher.ts --version "${{ needs.preflight.outputs.version }}" --output-dir "$RUNNER_TEMP/npm-packages"

# import.meta.main is defined because the runner's own Node may predate it.
- name: Bundle publish scripts
run: |
set -euo pipefail
mkdir -p publish-tools
cp "$RUNNER_TEMP/npm-packages/t3.tgz" .github/scripts/mirror-play-apk.cjs publish-tools/
banner="import{createRequire as __cr}from'node:module';const require=__cr(import.meta.url);"
for script in merge-update-manifests notify-discord-release; do
scripts/node_modules/.bin/esbuild "scripts/$script.ts" --bundle --platform=node --format=esm \
--target=node20 --define:import.meta.main=true --banner:js="$banner" \
--outfile="publish-tools/$script.mjs"
done

- name: Upload publish tools
uses: actions/upload-artifact@v7
with:
name: publish-tools
path: publish-tools/*
if-no-files-found: error
retention-days: 1

release:
name: Publish GitHub Release
needs:
Expand All @@ -747,30 +799,19 @@ jobs:
desktop_linux_arm64,
desktop_win_x64,
desktop_win_arm64,
prepare_publish,
]
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.quality.result == 'success' && needs.test.result == 'success' && needs.test_web.result == 'success' && needs.test_server.result == 'success' && needs.desktop_mac_arm64.result == 'success' && needs.desktop_mac_x64.result == 'success' && needs.desktop_linux_x64.result == 'success' && needs.desktop_linux_arm64.result == 'success' && needs.desktop_win_x64.result == 'success' && needs.desktop_win_arm64.result == 'success' }}
if: ${{ !failure() && !cancelled() && needs.preflight.result == 'success' && needs.relay_public_config.result == 'success' && needs.quality.result == 'success' && needs.test.result == 'success' && needs.test_web.result == 'success' && needs.test_server.result == 'success' && needs.desktop_mac_arm64.result == 'success' && needs.desktop_mac_x64.result == 'success' && needs.desktop_linux_x64.result == 'success' && needs.desktop_linux_arm64.result == 'success' && needs.desktop_win_x64.result == 'success' && needs.desktop_win_arm64.result == 'success' && needs.prepare_publish.result == 'success' }}
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 30
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.ref }}
sparse-checkout: |
/*
!/.repos/
sparse-checkout-cone-mode: false

- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
- name: Download publish tools
uses: actions/download-artifact@v8
with:
node-version-file: package.json
cache: true
run-install: |
args:
- --filter=@t3tools/scripts...
name: publish-tools
path: ${{ runner.temp }}/publish-tools

- name: Download all desktop artifacts
uses: actions/download-artifact@v8
Expand All @@ -795,7 +836,7 @@ jobs:
APK_OUTPUT_DIR: release-assets
run: |
if [ -n "$GOOGLE_PLAY_SERVICE_ACCOUNT_JSON" ]; then
node .github/scripts/mirror-play-apk.cjs
node "$RUNNER_TEMP/publish-tools/mirror-play-apk.cjs"
fi

# Installers verify archives against this file, so it is written from the
Expand Down Expand Up @@ -841,7 +882,7 @@ jobs:
for x64_manifest in release-assets/*-mac-x64.yml; do
arm64_manifest="${x64_manifest%-x64.yml}.yml"
if [[ -f "$arm64_manifest" ]]; then
node scripts/merge-update-manifests.ts --platform mac "$arm64_manifest" "$x64_manifest"
node "$RUNNER_TEMP/publish-tools/merge-update-manifests.mjs" --platform mac "$arm64_manifest" "$x64_manifest"
rm -f "$x64_manifest"
fi
done
Expand All @@ -854,7 +895,7 @@ jobs:
arm64_manifest="${x64_manifest%-x64.yml}-arm64.yml"
merged_manifest="${x64_manifest%-win-x64.yml}.yml"
if [[ -f "$arm64_manifest" ]]; then
node scripts/merge-update-manifests.ts --platform win "$x64_manifest" "$arm64_manifest" "$merged_manifest"
node "$RUNNER_TEMP/publish-tools/merge-update-manifests.mjs" --platform win "$x64_manifest" "$arm64_manifest" "$merged_manifest"
rm -f "$x64_manifest" "$arm64_manifest"
else
mv "$x64_manifest" "$merged_manifest"
Expand Down Expand Up @@ -950,31 +991,29 @@ jobs:
contents: read
id-token: write
steps:
- name: Checkout
# Only for the Node version, whose npm must support trusted publishing.
- name: Checkout package.json
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.ref }}
sparse-checkout: |
/*
!/.repos/
sparse-checkout: package.json
sparse-checkout-cone-mode: false

- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
- name: Setup Node
uses: actions/setup-node@v7
with:
node-version-file: package.json
cache: true
run-install: |
args:
- --filter=t3...
- --filter=@t3tools/scripts...

- name: Build the npm launcher
run: node scripts/build-npm-launcher.ts --version "${{ needs.preflight.outputs.version }}" --output-dir npm-packages
- name: Download publish tools
uses: actions/download-artifact@v8
with:
name: publish-tools
path: ${{ runner.temp }}/publish-tools

- name: Publish the npm launcher
working-directory: ${{ runner.temp }}/publish-tools
run: |
if ! node apps/server/scripts/cli.ts publish --packages-dir npm-packages --tag "${{ needs.preflight.outputs.cli_dist_tag }}" --provenance --verbose; then
if ! npm publish --access public --tag "${{ needs.preflight.outputs.cli_dist_tag }}" --provenance ./t3.tgz; then
echo "::error::npm publish failed. Make sure package t3 has a trusted publisher registered for .github/workflows/release.yml (see docs/operations/release.md)." >&2
exit 1
fi
Expand Down Expand Up @@ -1110,13 +1149,11 @@ jobs:
T3CODE_WEB_NIGHTLY_DOMAIN: ${{ vars.T3CODE_WEB_NIGHTLY_DOMAIN }}
VERCEL_TEAM_SLUG: ${{ vars.VERCEL_TEAM_SLUG }}
steps:
- name: Checkout
- name: Checkout package.json
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.ref }}
sparse-checkout: |
/*
!/.repos/
sparse-checkout: package.json
sparse-checkout-cone-mode: false

- name: Setup Vite+
Expand Down Expand Up @@ -1370,23 +1407,11 @@ jobs:
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v6
with:
ref: ${{ needs.preflight.outputs.ref }}
sparse-checkout: |
/*
!/.repos/
sparse-checkout-cone-mode: false

- name: Setup Vite+
uses: voidzero-dev/setup-vp@v1
- name: Download publish tools
uses: actions/download-artifact@v8
with:
node-version-file: package.json
cache: true
run-install: |
args:
- --filter=@t3tools/scripts...
name: publish-tools
path: ${{ runner.temp }}/publish-tools

- name: Read published release notes
if: needs.preflight.outputs.is_prerelease == 'true'
Expand All @@ -1413,7 +1438,7 @@ jobs:
DISCORD_MENTION_ROLE_ID: ${{ secrets.DISCORD_RELEASE_NIGHTLY_ROLE_ID }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }}
run: |
node scripts/notify-discord-release.ts prerelease \
node "$RUNNER_TEMP/publish-tools/notify-discord-release.mjs" prerelease \
--role-id "$DISCORD_MENTION_ROLE_ID" \
--release-name "${{ needs.preflight.outputs.release_name }}" \
--release-version "${{ needs.preflight.outputs.version }}" \
Expand All @@ -1428,7 +1453,7 @@ jobs:
DISCORD_MENTION_ROLE_ID: ${{ secrets.DISCORD_RELEASE_LATEST_ROLE_ID }}
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_RELEASE_WEBHOOK_URL }}
run: |
node scripts/notify-discord-release.ts latest \
node "$RUNNER_TEMP/publish-tools/notify-discord-release.mjs" latest \
--role-id "$DISCORD_MENTION_ROLE_ID" \
--release-name "${{ needs.preflight.outputs.release_name }}" \
--release-version "${{ needs.preflight.outputs.version }}" \
Expand Down
48 changes: 2 additions & 46 deletions apps/server/scripts/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { DEVELOPMENT_ICON_OVERRIDES } from "../../../scripts/lib/brand-assets.ts
import { findEsmImportsOfExternalPackages } from "../../../scripts/lib/cli-executable-imports.ts";
import { resolveSpawnCommand } from "@t3tools/shared/shell";
import {
ServerCliBuildAssetMissingError,
ServerCliCommandExitError,
ServerCliDevelopmentIconSourceMissingError,
ServerCliDevelopmentIconTargetMissingError,
Expand Down Expand Up @@ -153,52 +152,9 @@ const buildExeCmd = Command.make(
),
);

/**
* Publishes the `t3.tgz` launcher scripts/build-npm-launcher.ts produced. The
* launcher downloads its executable from the GitHub Release, so the release
* workflow runs this only after that release is published.
*/
const publishCmd = Command.make(
"publish",
{
packagesDir: Flag.String("packages-dir").pipe(
Flag.withDescription("Output dir of scripts/build-npm-launcher.ts."),
),
tag: Flag.String("tag").pipe(Flag.withDefault("latest")),
access: Flag.String("access").pipe(Flag.withDefault("public")),
provenance: Flag.Boolean("provenance").pipe(Flag.withDefault(false)),
dryRun: Flag.Boolean("dry-run").pipe(Flag.withDefault(false)),
verbose: Flag.Boolean("verbose").pipe(Flag.withDefault(false)),
},
(config) =>
Effect.gen(function* () {
const path = yield* Path.Path;
const fs = yield* FileSystem.FileSystem;
const packagesDir = path.resolve(config.packagesDir);
const launcherTarball = path.join(packagesDir, "t3.tgz");
if (!(yield* fs.exists(launcherTarball))) {
return yield* new ServerCliBuildAssetMissingError({ assetPath: launcherTarball });
}

const args = ["publish", "--access", config.access, "--tag", config.tag];
if (config.provenance) args.push("--provenance");
if (config.dryRun) args.push("--dry-run");
const spawnCommand = yield* resolveSpawnCommand("npm", [...args, launcherTarball]);
yield* Effect.log(`[cli] npm ${args.join(" ")} t3.tgz`);
yield* runCommand(
ChildProcess.make(spawnCommand.command, spawnCommand.args, {
cwd: packagesDir,
stdout: config.verbose ? "inherit" : "ignore",
stderr: "inherit",
shell: spawnCommand.shell,
}),
);
}),
).pipe(Command.withDescription("Publish the t3 launcher to npm."));

const cli = Command.make("cli").pipe(
Command.withDescription("T3 server build & publish CLI."),
Command.withSubcommands([buildCmd, buildExeCmd, publishCmd]),
Command.withDescription("T3 server build CLI."),
Command.withSubcommands([buildCmd, buildExeCmd]),
);

Command.run(cli, { version: "0.0.0" }).pipe(
Expand Down
11 changes: 0 additions & 11 deletions apps/server/scripts/cliErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,17 +36,6 @@ export class ServerCliDevelopmentIconTargetMissingError extends Schema.TaggedErr
}
}

export class ServerCliBuildAssetMissingError extends Schema.TaggedError<ServerCliBuildAssetMissingError>()(
"ServerCliBuildAssetMissingError",
{
assetPath: Schema.String,
},
) {
override get message(): string {
return `Missing build asset: ${this.assetPath}. Run the build subcommand first.`;
}
}

export class ServerCliExecutableImportError extends Schema.TaggedError<ServerCliExecutableImportError>()(
"ServerCliExecutableImportError",
{
Expand Down
6 changes: 3 additions & 3 deletions docs/operations/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -514,9 +514,9 @@ blockmaps, with a 60 MB maximum for a representative sidecar-to-sidecar update.

## 0) npm OIDC trusted publishing setup (CLI)

After the GitHub Release is published, the workflow runs `node scripts/build-npm-launcher.ts`, then
`node apps/server/scripts/cli.ts publish --packages-dir npm-packages`, which runs `npm publish` on
`t3.tgz`. That is the only package published per release.
`prepare_publish` builds `t3.tgz` with `node scripts/build-npm-launcher.ts` alongside the desktop
builds. After the GitHub Release is published, `publish_cli` runs `npm publish` on it. That is the
only package published per release.

Checklist:

Expand Down
Loading