feat: add custom asset filename (#1099)

Co-authored-by: FabianLars <github@fabianlars.de>
This commit is contained in:
Lete
2025-07-06 02:26:34 +08:00
committed by GitHub
parent 564aea5a80
commit 5b1138d93c
11 changed files with 383 additions and 131 deletions

View File

@@ -0,0 +1,5 @@
---
action: patch
---
Added the `assetNamePattern` config that allows setting a template specifying how uploaded assets will be named in the release.

View File

@@ -1,4 +1,4 @@
name: 'real world tests'
name: "real world tests"
on:
workflow_dispatch:
@@ -13,12 +13,12 @@ jobs:
fail-fast: false
matrix:
include:
- platform: 'macos-latest'
args: '--verbose --target universal-apple-darwin'
- platform: 'ubuntu-22.04'
args: '--verbose'
- platform: 'windows-latest'
args: '--verbose'
- platform: "macos-latest"
args: "--verbose --target universal-apple-darwin"
- platform: "ubuntu-22.04"
args: "--verbose"
- platform: "windows-latest"
args: "--verbose"
runs-on: ${{ matrix.platform }}
steps:
@@ -75,8 +75,8 @@ jobs:
includeDebug: true
includeRelease: false
tagName: ${{ !github.event.pull_request.head.repo.fork && 'example-with-tauri-v__VERSION__' || '' }}
releaseName: 'Release example with preconfigured Tauri app v__VERSION__ for tauri-v1'
releaseBody: 'See the assets to download this version and install.'
releaseName: "Release example with preconfigured Tauri app v__VERSION__ for tauri-v1"
releaseBody: "See the assets to download this version and install."
releaseDraft: true
args: ${{ matrix.args }}
@@ -96,8 +96,8 @@ jobs:
includeDebug: true
includeRelease: false
tagName: ${{ !github.event.pull_request.head.repo.fork && 'example-v__VERSION__' || '' }}
releaseName: 'Release example app v__VERSION__ for tauri-v1'
releaseBody: 'See the assets to download this version and install.'
releaseName: "Release example app v__VERSION__ for tauri-v1"
releaseBody: "See the assets to download this version and install."
releaseDraft: true
args: ${{ matrix.args }}
@@ -106,12 +106,12 @@ jobs:
fail-fast: false
matrix:
include:
- platform: 'macos-latest'
args: '--verbose --target universal-apple-darwin'
- platform: 'ubuntu-22.04'
args: '--verbose'
- platform: 'windows-latest'
args: '--verbose'
- platform: "macos-latest"
args: "--verbose --target universal-apple-darwin"
- platform: "ubuntu-22.04"
args: "--verbose"
- platform: "windows-latest"
args: "--verbose"
runs-on: ${{ matrix.platform }}
steps:
@@ -168,12 +168,13 @@ jobs:
includeDebug: true
includeRelease: false
tagName: ${{ !github.event.pull_request.head.repo.fork && 'example-with-tauri-v__VERSION__' || '' }}
releaseName: 'Release example with preconfigured Tauri app v__VERSION__ for tauri-v2'
releaseBody: 'See the assets to download this version and install.'
releaseName: "Release example with preconfigured Tauri app v__VERSION__ for tauri-v2"
releaseBody: "See the assets to download this version and install."
releaseDraft: true
args: ${{ matrix.args }}
updaterJsonKeepUniversal: true
retryAttempts: 1
assetNamePattern: "[name]x[version]x[platform]x[arch]x[ext]"
- name: Frontend-only Project
uses: ./
@@ -191,7 +192,7 @@ jobs:
includeDebug: true
includeRelease: false
tagName: ${{ !github.event.pull_request.head.repo.fork && 'example-v__VERSION__' || '' }}
releaseName: 'Release example app v__VERSION__ for tauri-v2'
releaseBody: 'See the assets to download this version and install.'
releaseName: "Release example app v__VERSION__ for tauri-v2"
releaseBody: "See the assets to download this version and install."
releaseDraft: true
args: ${{ matrix.args }}

View File

@@ -114,17 +114,18 @@ These inputs allow you to change how your Tauri project will be build.
These inputs allow you to modify the GitHub release.
| Name | Required | Description | Type | Default |
| ------------------ | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------------- |
| `releaseId` | false | The id of the release to upload artifacts as release assets. If set, `tagName` and `releaseName` will not be considered to find a release. | number | |
| `tagName` | false | The tag name of the release to upload/create or the tag of the release belonging to `releaseId` | string | |
| `releaseName` | false | The name of the release to create. Required if there's no existing release for `tagName` | string | |
| `releaseBody` | false | The body of the release to create | string | |
| `releaseDraft` | false | Whether the release to find or create is a draft or not | bool | false |
| `prerelease` | false | Whether the release to create is a prerelease or not | bool | false |
| `releaseCommitish` | false | Any branch or commit SHA the Git tag is created from, unused if the Git tag already exists. | string | SHA of current commit |
| `owner` | false | The account owner of the repository the release will be uploaded to. Requires `GITHUB_TOKEN` in env and a `releaseCommitish` target if it doesn't match the current repo. | string | owner of the current repo |
| `repo` | false | The name of the repository the release will be uploaded to. Requires `GITHUB_TOKEN` in env and a `releaseCommitish` target if it doesn't match the current repo. | string | name of the current repo |
| Name | Required | Description | Type | Default |
| ------------------ | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------------------------------ |
| `releaseId` | false | The id of the release to upload artifacts as release assets. If set, `tagName` and `releaseName` will not be considered to find a release. | number | |
| `tagName` | false | The tag name of the release to upload/create or the tag of the release belonging to `releaseId` | string | |
| `releaseName` | false | The name of the release to create. Required if there's no existing release for `tagName` | string | |
| `releaseBody` | false | The body of the release to create | string | |
| `releaseDraft` | false | Whether the release to find or create is a draft or not | bool | false |
| `prerelease` | false | Whether the release to create is a prerelease or not | bool | false |
| `releaseCommitish` | false | Any branch or commit SHA the Git tag is created from, unused if the Git tag already exists. | string | SHA of current commit |
| `owner` | false | The account owner of the repository the release will be uploaded to. Requires `GITHUB_TOKEN` in env and a `releaseCommitish` target if it doesn't match the current repo. | string | owner of the current repo |
| `repo` | false | The name of the repository the release will be uploaded to. Requires `GITHUB_TOKEN` in env and a `releaseCommitish` target if it doesn't match the current repo. | string | name of the current repo |
| `assetNamePattern` | false | The naming pattern to use for the uploaded assets | string | `[name]_v[version]_[platform]_[arch][ext]` |
## Outputs

View File

@@ -58,6 +58,8 @@ inputs:
description: 'The account owner of the repository'
repo:
description: 'The name of the repository'
assetNamePattern:
description: 'The naming pattern to use for the uploaded assets'
outputs:
releaseId:
description: 'The ID of the created release'

4
dist/index.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,7 @@ import { join } from 'node:path';
import { initProject } from './init-project';
import { getRunner } from './runner';
import {
createArtifact,
getInfo,
getTargetDir,
getTargetInfo,
@@ -107,11 +108,42 @@ export async function buildProject(
}
artifacts = [
join(artifactsPath, `bundle/dmg/${app.name}_${app.version}_${arch}.dmg`),
join(artifactsPath, `bundle/macos/${app.name}.app`),
join(artifactsPath, `bundle/macos/${app.name}.app.tar.gz`),
join(artifactsPath, `bundle/macos/${app.name}.app.tar.gz.sig`),
].map((path) => ({ path, arch }));
createArtifact({
path: join(
artifactsPath,
`bundle/dmg/${app.name}_${app.version}_${arch}.dmg`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(artifactsPath, `bundle/macos/${app.name}.app`),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(artifactsPath, `bundle/macos/${app.name}.app.tar.gz`),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(artifactsPath, `bundle/macos/${app.name}.app.tar.gz.sig`),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
];
} else if (targetInfo.platform === 'windows') {
if (arch.startsWith('i')) {
arch = 'x86';
@@ -132,28 +164,56 @@ export async function buildProject(
langs = Object.keys(app.wixLanguage);
}
const winArtifacts: string[] = [];
const winArtifacts: Artifact[] = [];
// wix v1
if (app.version != app.wixAppVersion) {
langs.forEach((lang) => {
winArtifacts.push(
join(
artifactsPath,
`bundle/msi/${app.name}_${app.wixAppVersion}_${arch}_${lang}.msi`,
),
join(
artifactsPath,
`bundle/msi/${app.name}_${app.wixAppVersion}_${arch}_${lang}.msi.sig`,
),
join(
artifactsPath,
`bundle/msi/${app.name}_${app.wixAppVersion}_${arch}_${lang}.msi.zip`,
),
join(
artifactsPath,
`bundle/msi/${app.name}_${app.wixAppVersion}_${arch}_${lang}.msi.zip.sig`,
),
createArtifact({
path: join(
artifactsPath,
`bundle/msi/${app.name}_${app.wixAppVersion}_${arch}_${lang}.msi`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/msi/${app.name}_${app.wixAppVersion}_${arch}_${lang}.msi.sig`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/msi/${app.name}_${app.wixAppVersion}_${arch}_${lang}.msi.zip`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/msi/${app.name}_${app.wixAppVersion}_${arch}_${lang}.msi.zip.sig`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
);
});
}
@@ -161,45 +221,101 @@ export async function buildProject(
// wix v2
langs.forEach((lang) => {
winArtifacts.push(
join(
artifactsPath,
`bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi`,
),
join(
artifactsPath,
`bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.sig`,
),
join(
artifactsPath,
`bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.zip`,
),
join(
artifactsPath,
`bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.zip.sig`,
),
createArtifact({
path: join(
artifactsPath,
`bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.sig`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.zip`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/msi/${app.name}_${app.version}_${arch}_${lang}.msi.zip.sig`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
);
});
winArtifacts.push(
join(
artifactsPath,
`bundle/nsis/${app.name}_${app.version}_${arch}-setup.exe`,
),
join(
artifactsPath,
`bundle/nsis/${app.name}_${app.version}_${arch}-setup.exe.sig`,
),
join(
artifactsPath,
`bundle/nsis/${app.name}_${app.version}_${arch}-setup.nsis.zip`,
),
join(
artifactsPath,
`bundle/nsis/${app.name}_${app.version}_${arch}-setup.nsis.zip.sig`,
),
createArtifact({
path: join(
artifactsPath,
`bundle/nsis/${app.name}_${app.version}_${arch}-setup.exe`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/nsis/${app.name}_${app.version}_${arch}-setup.exe.sig`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/nsis/${app.name}_${app.version}_${arch}-setup.nsis.zip`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/nsis/${app.name}_${app.version}_${arch}-setup.nsis.zip.sig`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch,
version: app.version,
}),
);
artifacts = winArtifacts.map((path) => ({ path, arch }));
artifacts = winArtifacts;
} else {
const debianArch =
arch === 'x64' || arch === 'x86_64'
@@ -233,94 +349,142 @@ export async function buildProject(
: arch;
artifacts = [
{
createArtifact({
path: join(
artifactsPath,
`bundle/deb/${app.name}_${app.version}_${debianArch}.deb`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch: debianArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/rpm/${app.name}-${app.version}-${app.rpmRelease}.${rpmArch}.rpm`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch: rpmArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch: appImageArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage.sig`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch: appImageArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage.tar.gz`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch: appImageArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/appimage/${app.name}_${app.version}_${appImageArch}.AppImage.tar.gz.sig`,
),
name: app.name,
debug,
platform: targetInfo.platform,
arch: appImageArch,
},
version: app.version,
}),
];
if (app.name != linuxFileAppName) {
artifacts.push(
{
createArtifact({
path: join(
artifactsPath,
`bundle/deb/${linuxFileAppName}_${app.version}_${debianArch}.deb`,
),
name: linuxFileAppName,
debug,
platform: targetInfo.platform,
arch: debianArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/rpm/${linuxFileAppName}-${app.version}-${app.rpmRelease}.${rpmArch}.rpm`,
),
name: linuxFileAppName,
debug,
platform: targetInfo.platform,
arch: rpmArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/appimage/${linuxFileAppName}_${app.version}_${appImageArch}.AppImage`,
),
name: linuxFileAppName,
debug,
platform: targetInfo.platform,
arch: appImageArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/appimage/${linuxFileAppName}_${app.version}_${appImageArch}.AppImage.sig`,
),
name: linuxFileAppName,
debug,
platform: targetInfo.platform,
arch: appImageArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/appimage/${linuxFileAppName}_${app.version}_${appImageArch}.AppImage.tar.gz`,
),
name: linuxFileAppName,
debug,
platform: targetInfo.platform,
arch: appImageArch,
},
{
version: app.version,
}),
createArtifact({
path: join(
artifactsPath,
`bundle/appimage/${linuxFileAppName}_${app.version}_${appImageArch}.AppImage.tar.gz.sig`,
),
name: linuxFileAppName,
debug,
platform: targetInfo.platform,
arch: appImageArch,
},
version: app.version,
}),
);
}
}

View File

@@ -33,6 +33,7 @@ async function run(): Promise<void> {
const tauriScript = core.getInput('tauriScript');
const args = stringArgv(core.getInput('args'));
const bundleIdentifier = core.getInput('bundleIdentifier');
const assetNamePattern = core.getInput('assetNamePattern');
let tagName = core.getInput('tagName').replace('refs/tags/', '');
let releaseId = Number(core.getInput('releaseId'));
@@ -192,6 +193,7 @@ async function run(): Promise<void> {
releaseId,
artifacts,
retryAttempts,
assetNamePattern,
);
if (includeUpdaterJson) {
@@ -208,6 +210,7 @@ async function run(): Promise<void> {
updaterJsonPreferNsis,
updaterJsonKeepUniversal,
retryAttempts,
assetNamePattern,
);
}
} else {

5
src/types.d.ts vendored
View File

@@ -10,7 +10,12 @@ export interface Application {
export interface Artifact {
path: string;
name: string;
mode: string;
platform: Exclude<TargetPlatform, 'macos'> | 'darwin';
arch: string;
ext: string;
version: string;
}
export interface BuildOptions {

View File

@@ -11,6 +11,7 @@ export async function uploadAssets(
releaseId: number,
assets: Artifact[],
retryAttempts: number,
assetNamePattern?: string,
) {
if (process.env.GITHUB_TOKEN === undefined) {
throw new Error('GITHUB_TOKEN is required');
@@ -36,7 +37,10 @@ export async function uploadAssets(
'content-length': contentLength(asset.path),
};
const assetName = getAssetName(asset.path);
const assetName =
asset.name === 'latest.json'
? 'latest.json'
: getAssetName(asset, assetNamePattern);
const existingAsset = existingAssets.find(
(a) =>

View File

@@ -7,6 +7,7 @@ import { uploadAssets } from './upload-release-assets';
import { getAssetName } from './utils';
import type { Artifact, TargetInfo } from './types';
import { createArtifact } from './utils';
type Platform = {
signature: string;
@@ -35,6 +36,7 @@ export async function uploadVersionJSON(
updaterJsonPreferNsis: boolean,
updaterJsonKeepUniversal: boolean,
retryAttempts: number,
assetNamePattern?: string,
) {
if (process.env.GITHUB_TOKEN === undefined) {
throw new Error('GITHUB_TOKEN is required');
@@ -89,7 +91,7 @@ export async function uploadVersionJSON(
// Assets matching artifacts generated by this action
const filteredAssets = [];
for (const artifact of artifacts) {
const assetName = getAssetName(artifact.path)
const assetName = getAssetName(artifact, assetNamePattern)
.trim()
.replace(/[ ()[\]{}]/g, '.')
.replace(/\.\./g, '.')
@@ -204,11 +206,14 @@ export async function uploadVersionJSON(
});
}
await uploadAssets(
owner,
repo,
releaseId,
[{ path: versionFile, arch: '' }],
retryAttempts,
);
const artifact = createArtifact({
path: versionFile,
name: versionFilename,
debug: false,
platform: targetInfo.platform,
arch: '',
version,
});
await uploadAssets(owner, repo, releaseId, [artifact], retryAttempts);
}

View File

@@ -1,5 +1,12 @@
import { existsSync, readFileSync } from 'node:fs';
import path, { join, normalize, resolve, sep } from 'node:path';
import path, {
basename,
extname,
join,
normalize,
resolve,
sep,
} from 'node:path';
import { parse as parseToml } from '@iarna/toml';
import { execa } from 'execa';
@@ -8,6 +15,7 @@ import { globbySync } from 'globby';
import { TauriConfig } from './config';
import type {
Artifact,
CargoConfig,
CargoManifest,
Info,
@@ -37,7 +45,7 @@ export const extensions = [
];
/*** helper functions ***/
export function getAssetName(assetPath: string) {
export function parseAsset(assetPath: string) {
const basename = path.basename(assetPath);
const exts = extensions.filter((s) => basename.includes(s));
const ext = exts[0] || path.extname(assetPath);
@@ -46,19 +54,73 @@ export function getAssetName(assetPath: string) {
let arch = '';
if (ext === '.app.tar.gz.sig' || ext === '.app.tar.gz') {
if (assetPath.includes('universal-apple-darwin')) {
arch = '_universal';
arch = 'universal';
} else if (assetPath.includes('aarch64-apple-darwin')) {
arch = '_aarch64';
arch = 'aarch64';
} else if (assetPath.includes('x86_64-apple-darwin')) {
arch = '_x64';
arch = 'x64';
} else {
arch = process.arch === 'arm64' ? '_aarch64' : '_x64';
arch = process.arch === 'arm64' ? 'aarch64' : 'x64';
}
}
return assetPath.includes(`${path.sep}debug${path.sep}`)
? `${filename}-debug${arch}${ext}`
: `${filename}${arch}${ext}`;
return { basename, ext, filename, arch };
}
export function renderNamePattern(
pattern: string,
replacements: Record<string, string>,
) {
return pattern.replace(/\[(\w+)]/g, (match, type: string) => {
if (!Object.prototype.hasOwnProperty.call(replacements, type)) {
return match;
}
const replacement = replacements[type];
return replacement;
});
}
export function getAssetName(asset: Artifact, pattern?: string) {
const debugPattern = asset.mode === 'debug' ? '_[mode]' : '';
const DEFAULT_PATTERN = `[name]_v[version]${debugPattern}_[platform]_[arch][ext]`;
pattern = pattern || DEFAULT_PATTERN;
const filename = renderNamePattern(
pattern,
asset as unknown as Record<string, string>,
);
return filename;
}
export function createArtifact({
path,
name,
debug,
platform,
arch,
version,
}: {
path: string;
name: string;
debug: boolean;
platform: TargetPlatform;
arch: string;
version: string;
}): Artifact {
const baseName = basename(path);
const exts = extensions.filter((s) => baseName.includes(s));
const ext = exts[0] || extname(path);
return {
path,
name,
mode: debug ? 'debug' : 'release',
platform: platform === 'macos' ? 'darwin' : platform,
arch,
ext,
version,
};
}
export function getPackageJson(root: string) {