refactor: Merge workspace into single package. (#362)

* refactor repo into one package

* take pnpm for a test drive

* add tslib again

* fix dist path in action.yml

* split up core script. add eslint

* don't early return artifacts on macos

* up node version

* update deps

* fix change files

* rebuild dist
This commit is contained in:
Fabian-Lars
2023-02-06 13:44:53 +01:00
committed by GitHub
parent fa6292738e
commit c5c0e27d68
71 changed files with 3373 additions and 3484 deletions

View File

@@ -6,7 +6,7 @@
"getPublishedVersion": "npm view ${ pkgFile.pkg.name } version", "getPublishedVersion": "npm view ${ pkgFile.pkg.name } version",
"prepublish": [ "prepublish": [
{ {
"command": "yarn", "command": "pnpm install",
"dryRunCommand": true "dryRunCommand": true
}, },
{ {
@@ -29,18 +29,8 @@
} }
}, },
"packages": { "packages": {
"@tauri-apps/action-core": {
"path": "./packages/core",
"manager": "javascript",
"assets": [
{
"path": "./packages/core/tauri-apps-action-core-${ pkgFile.version }.tgz",
"name": "action-core-${ pkgFile.version }.tgz"
}
]
},
"action": { "action": {
"path": "./packages/action", "path": "./",
"manager": "javascript", "manager": "javascript",
"publish": [ "publish": [
"git tag v${ pkgFile.versionMajor } -f", "git tag v${ pkgFile.versionMajor } -f",

View File

@@ -1,5 +1,4 @@
--- ---
"@tauri-apps/action-core": patch
"action": patch "action": patch
--- ---

View File

@@ -1,5 +1,5 @@
--- ---
"@tauri-apps/action-core": patch 'action': patch
--- ---
Replace `_` and `.` with `-` in the product name on Linux. Replace `_` and `.` with `-` in the product name on Linux.

View File

@@ -1,6 +1,5 @@
--- ---
"@tauri-apps/action-core": patch 'action': patch
"action": patch
--- ---
Fixes usage with `vue-cli-plugin-tauri`. Fixes usage with `vue-cli-plugin-tauri`.

20
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,20 @@
module.exports = {
env: {
node: true,
es2021: true,
},
extends: [
'prettier',
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
plugins: ['@typescript-eslint'],
rules: {
'@typescript-eslint/ban-ts-comment': 'off',
},
};

View File

@@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: covector status - name: covector status

View File

@@ -9,13 +9,17 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- uses: actions/setup-node@v1 - uses: actions/setup-node@v3
with: with:
node-version: 12 node-version: 16
registry-url: 'https://registry.npmjs.org' registry-url: 'https://registry.npmjs.org'
- name: install pnpm
uses: pnpm/action-setup@v2
with:
version: 7.x.x
- name: git config - name: git config
run: | run: |
git config --global user.name "${{ github.event.pusher.name }}" git config --global user.name "${{ github.event.pusher.name }}"

View File

@@ -15,18 +15,22 @@ jobs:
- name: setup node - name: setup node
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: 14 node-version: 16
- name: install Rust stable - name: install Rust stable
uses: actions-rs/toolchain@v1 uses: actions-rs/toolchain@v1
with: with:
toolchain: stable toolchain: stable
- name: install pnpm
uses: pnpm/action-setup@v2
with:
version: 7.x.x
- name: install dependencies (ubuntu only) - name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-latest' if: matrix.platform == 'ubuntu-latest'
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: install example dependencies - name: install example dependencies
run: yarn run: pnpm install
working-directory: ./packages/action/__fixtures__/example working-directory: ./packages/action/__fixtures__/example
- uses: ./ - uses: ./
env: env:

7
.gitignore vendored
View File

@@ -44,10 +44,3 @@ target
# lockfiles, as a lib we don't need to lock # lockfiles, as a lib we don't need to lock
yarn.lock yarn.lock
# rollup output
dist
# but we want the action dist
!packages/action/dist
!packages/action/dist/**
!packages/action/__fixtures__/**/dist

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -50,4 +50,4 @@ outputs:
description: 'The paths of the generated artifacts' description: 'The paths of the generated artifacts'
runs: runs:
using: 'node16' using: 'node16'
main: 'packages/action/dist/index.js' main: 'dist/index.js'

7
dist/index.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +1,47 @@
{ {
"private": true, "name": "tauri-action",
"workspaces": [ "version": "0.3.1",
"packages/core", "description": "Tauri GitHub Action",
"packages/cli", "author": "Lucas Nogueira <lucas@tauri.studio>",
"packages/action" "license": "MIT",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "ncc build src/index.ts -o dist -m",
"lint": "eslint src/**"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tauri-apps/tauri-action.git"
},
"keywords": [
"actions",
"tauri"
], ],
"dependencies": {
"@actions/core": "1.10.0",
"@actions/github": "5.1.1",
"@iarna/toml": "2.2.5",
"execa": "6.1.0",
"glob-gitignore": "^1.0.14",
"ignore": "5.2.4",
"json5": "2.2.3",
"string-argv": "0.3.1",
"tslib": "2.5.0"
},
"devDependencies": { "devDependencies": {
"covector": "0.7.3", "@types/node": "16.18.11",
"prettier": "^2.8.0" "@typescript-eslint/eslint-plugin": "5.49.0",
"@typescript-eslint/parser": "5.49.0",
"@vercel/ncc": "0.36.1",
"covector": "0.8.0",
"eslint": "8.33.0",
"eslint-config-prettier": "8.6.0",
"eslint-config-standard-with-typescript": "33.0.0",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-n": "15.6.1",
"eslint-plugin-promise": "6.1.1",
"prettier": "2.8.3",
"typescript": "4.9.4"
} }
} }

View File

@@ -1,450 +0,0 @@
export const id = 543;
export const ids = [543];
export const modules = {
/***/ 543:
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "toFormData": () => (/* binding */ toFormData)
/* harmony export */ });
/* harmony import */ var fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(7907);
/* harmony import */ var formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6296);
let s = 0;
const S = {
START_BOUNDARY: s++,
HEADER_FIELD_START: s++,
HEADER_FIELD: s++,
HEADER_VALUE_START: s++,
HEADER_VALUE: s++,
HEADER_VALUE_ALMOST_DONE: s++,
HEADERS_ALMOST_DONE: s++,
PART_DATA_START: s++,
PART_DATA: s++,
END: s++
};
let f = 1;
const F = {
PART_BOUNDARY: f,
LAST_BOUNDARY: f *= 2
};
const LF = 10;
const CR = 13;
const SPACE = 32;
const HYPHEN = 45;
const COLON = 58;
const A = 97;
const Z = 122;
const lower = c => c | 0x20;
const noop = () => {};
class MultipartParser {
/**
* @param {string} boundary
*/
constructor(boundary) {
this.index = 0;
this.flags = 0;
this.onHeaderEnd = noop;
this.onHeaderField = noop;
this.onHeadersEnd = noop;
this.onHeaderValue = noop;
this.onPartBegin = noop;
this.onPartData = noop;
this.onPartEnd = noop;
this.boundaryChars = {};
boundary = '\r\n--' + boundary;
const ui8a = new Uint8Array(boundary.length);
for (let i = 0; i < boundary.length; i++) {
ui8a[i] = boundary.charCodeAt(i);
this.boundaryChars[ui8a[i]] = true;
}
this.boundary = ui8a;
this.lookbehind = new Uint8Array(this.boundary.length + 8);
this.state = S.START_BOUNDARY;
}
/**
* @param {Uint8Array} data
*/
write(data) {
let i = 0;
const length_ = data.length;
let previousIndex = this.index;
let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
const boundaryLength = this.boundary.length;
const boundaryEnd = boundaryLength - 1;
const bufferLength = data.length;
let c;
let cl;
const mark = name => {
this[name + 'Mark'] = i;
};
const clear = name => {
delete this[name + 'Mark'];
};
const callback = (callbackSymbol, start, end, ui8a) => {
if (start === undefined || start !== end) {
this[callbackSymbol](ui8a && ui8a.subarray(start, end));
}
};
const dataCallback = (name, clear) => {
const markSymbol = name + 'Mark';
if (!(markSymbol in this)) {
return;
}
if (clear) {
callback(name, this[markSymbol], i, data);
delete this[markSymbol];
} else {
callback(name, this[markSymbol], data.length, data);
this[markSymbol] = 0;
}
};
for (i = 0; i < length_; i++) {
c = data[i];
switch (state) {
case S.START_BOUNDARY:
if (index === boundary.length - 2) {
if (c === HYPHEN) {
flags |= F.LAST_BOUNDARY;
} else if (c !== CR) {
return;
}
index++;
break;
} else if (index - 1 === boundary.length - 2) {
if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
state = S.END;
flags = 0;
} else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
index = 0;
callback('onPartBegin');
state = S.HEADER_FIELD_START;
} else {
return;
}
break;
}
if (c !== boundary[index + 2]) {
index = -2;
}
if (c === boundary[index + 2]) {
index++;
}
break;
case S.HEADER_FIELD_START:
state = S.HEADER_FIELD;
mark('onHeaderField');
index = 0;
// falls through
case S.HEADER_FIELD:
if (c === CR) {
clear('onHeaderField');
state = S.HEADERS_ALMOST_DONE;
break;
}
index++;
if (c === HYPHEN) {
break;
}
if (c === COLON) {
if (index === 1) {
// empty header field
return;
}
dataCallback('onHeaderField', true);
state = S.HEADER_VALUE_START;
break;
}
cl = lower(c);
if (cl < A || cl > Z) {
return;
}
break;
case S.HEADER_VALUE_START:
if (c === SPACE) {
break;
}
mark('onHeaderValue');
state = S.HEADER_VALUE;
// falls through
case S.HEADER_VALUE:
if (c === CR) {
dataCallback('onHeaderValue', true);
callback('onHeaderEnd');
state = S.HEADER_VALUE_ALMOST_DONE;
}
break;
case S.HEADER_VALUE_ALMOST_DONE:
if (c !== LF) {
return;
}
state = S.HEADER_FIELD_START;
break;
case S.HEADERS_ALMOST_DONE:
if (c !== LF) {
return;
}
callback('onHeadersEnd');
state = S.PART_DATA_START;
break;
case S.PART_DATA_START:
state = S.PART_DATA;
mark('onPartData');
// falls through
case S.PART_DATA:
previousIndex = index;
if (index === 0) {
// boyer-moore derrived algorithm to safely skip non-boundary data
i += boundaryEnd;
while (i < bufferLength && !(data[i] in boundaryChars)) {
i += boundaryLength;
}
i -= boundaryEnd;
c = data[i];
}
if (index < boundary.length) {
if (boundary[index] === c) {
if (index === 0) {
dataCallback('onPartData', true);
}
index++;
} else {
index = 0;
}
} else if (index === boundary.length) {
index++;
if (c === CR) {
// CR = part boundary
flags |= F.PART_BOUNDARY;
} else if (c === HYPHEN) {
// HYPHEN = end boundary
flags |= F.LAST_BOUNDARY;
} else {
index = 0;
}
} else if (index - 1 === boundary.length) {
if (flags & F.PART_BOUNDARY) {
index = 0;
if (c === LF) {
// unset the PART_BOUNDARY flag
flags &= ~F.PART_BOUNDARY;
callback('onPartEnd');
callback('onPartBegin');
state = S.HEADER_FIELD_START;
break;
}
} else if (flags & F.LAST_BOUNDARY) {
if (c === HYPHEN) {
callback('onPartEnd');
state = S.END;
flags = 0;
} else {
index = 0;
}
} else {
index = 0;
}
}
if (index > 0) {
// when matching a possible boundary, keep a lookbehind reference
// in case it turns out to be a false lead
lookbehind[index - 1] = c;
} else if (previousIndex > 0) {
// if our boundary turned out to be rubbish, the captured lookbehind
// belongs to partData
const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
callback('onPartData', 0, previousIndex, _lookbehind);
previousIndex = 0;
mark('onPartData');
// reconsider the current character even so it interrupted the sequence
// it could be the beginning of a new sequence
i--;
}
break;
case S.END:
break;
default:
throw new Error(`Unexpected state entered: ${state}`);
}
}
dataCallback('onHeaderField');
dataCallback('onHeaderValue');
dataCallback('onPartData');
// Update properties for the next call
this.index = index;
this.state = state;
this.flags = flags;
}
end() {
if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
(this.state === S.PART_DATA && this.index === this.boundary.length)) {
this.onPartEnd();
} else if (this.state !== S.END) {
throw new Error('MultipartParser.end(): stream ended unexpectedly');
}
}
}
function _fileName(headerValue) {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
if (!m) {
return;
}
const match = m[2] || m[3] || '';
let filename = match.slice(match.lastIndexOf('\\') + 1);
filename = filename.replace(/%22/g, '"');
filename = filename.replace(/&#(\d{4});/g, (m, code) => {
return String.fromCharCode(code);
});
return filename;
}
async function toFormData(Body, ct) {
if (!/multipart/i.test(ct)) {
throw new TypeError('Failed to fetch');
}
const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
if (!m) {
throw new TypeError('no or bad content-type header, no multipart boundary');
}
const parser = new MultipartParser(m[1] || m[2]);
let headerField;
let headerValue;
let entryValue;
let entryName;
let contentType;
let filename;
const entryChunks = [];
const formData = new formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__/* .FormData */ .Ct();
const onPartData = ui8a => {
entryValue += decoder.decode(ui8a, {stream: true});
};
const appendToFile = ui8a => {
entryChunks.push(ui8a);
};
const appendFileToFormData = () => {
const file = new fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__/* .File */ .$B(entryChunks, filename, {type: contentType});
formData.append(entryName, file);
};
const appendEntryToFormData = () => {
formData.append(entryName, entryValue);
};
const decoder = new TextDecoder('utf-8');
decoder.decode();
parser.onPartBegin = function () {
parser.onPartData = onPartData;
parser.onPartEnd = appendEntryToFormData;
headerField = '';
headerValue = '';
entryValue = '';
entryName = '';
contentType = '';
filename = null;
entryChunks.length = 0;
};
parser.onHeaderField = function (ui8a) {
headerField += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderValue = function (ui8a) {
headerValue += decoder.decode(ui8a, {stream: true});
};
parser.onHeaderEnd = function () {
headerValue += decoder.decode();
headerField = headerField.toLowerCase();
if (headerField === 'content-disposition') {
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
if (m) {
entryName = m[2] || m[3] || '';
}
filename = _fileName(headerValue);
if (filename) {
parser.onPartData = appendToFile;
parser.onPartEnd = appendFileToFormData;
}
} else if (headerField === 'content-type') {
contentType = headerValue;
}
headerValue = '';
headerField = '';
};
for await (const chunk of Body) {
parser.write(chunk);
}
parser.end();
return formData;
}
/***/ })
};

File diff suppressed because one or more lines are too long

View File

@@ -1,34 +0,0 @@
{
"name": "tauri-action",
"version": "0.3.1",
"description": "Tauri GitHub Action",
"author": "Lucas Nogueira <lucas@tauri.studio>",
"license": "MIT",
"main": "dist/index.js",
"type": "module",
"scripts": {
"build": "ncc build src/index.ts -o dist -m"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tauri-apps/tauri-action.git"
},
"keywords": [
"actions",
"tauri"
],
"dependencies": {
"@actions/core": "1.10.0",
"@actions/github": "5.1.1",
"@tauri-apps/action-core": "0.3.1",
"node-fetch": "3.3.0",
"string-argv": "0.3.1",
"tslib": "2.4.1"
},
"devDependencies": {
"@types/node": "16.18.11",
"@vercel/ncc": "0.36.0",
"rollup": "2.79.1",
"typescript": "4.9.4"
}
}

View File

@@ -1,34 +0,0 @@
import path from 'path';
const extensions = [
'.app.tar.gz.sig',
'.app.tar.gz',
'.dmg',
'.AppImage.tar.gz.sig',
'.AppImage.tar.gz',
'.AppImage',
'.deb',
'.msi.zip.sig',
'.msi.zip',
'.msi',
];
export function getAssetName(assetPath: string) {
const basename = path.basename(assetPath);
const exts = extensions.filter((s) => basename.includes(s));
const ext = exts[0] || path.extname(assetPath);
const filename = basename.replace(ext, '');
let arch = '';
if (ext === '.app.tar.gz.sig' || ext === '.app.tar.gz') {
arch = assetPath.includes('universal-apple-darwin')
? '_universal'
: assetPath.includes('aarch64-apple-darwin')
? '_aarch64'
: '_x64';
}
return assetPath.includes(`${path.sep}debug${path.sep}`)
? `${filename}-debug${arch}${ext}`
: `${filename}${arch}${ext}`;
}

View File

@@ -1,8 +0,0 @@
{
"compilerOptions": {
"esModuleInterop": true,
"target": "es2015",
"moduleResolution": "node"
},
"include": ["."]
}

View File

@@ -1,2 +0,0 @@
import { run } from '../dist/index.js';
run();

View File

@@ -1,49 +0,0 @@
import { join, resolve } from 'path';
import { existsSync } from 'fs';
import { buildProject } from '@tauri-apps/action-core';
import type { BuildOptions } from '@tauri-apps/action-core';
import parseArgs from 'minimist';
export async function run(): Promise<void> {
const argv = parseArgs(process.argv.slice(2), {
string: [
'project-path',
'config-path',
'dist-path',
'icon-path',
'tauri-script',
],
boolean: ['global-tauri', 'include-debug'],
default: {
'config-path': 'tauri.conf.json',
'project-path': '',
},
});
const projectPath = resolve(process.cwd(), argv['project-path']);
const configPath = join(projectPath, argv['config-path']);
const distPath = argv['dist-path'];
const iconPath = argv['icon-path'];
const includeDebug = argv['include-debug'];
const tauriScript = argv['tauri-script'];
const args = argv._;
const options: BuildOptions = {
configPath: existsSync(configPath) ? configPath : null,
distPath,
iconPath,
tauriScript,
args,
};
const artifacts = await buildProject(projectPath, false, options);
if (includeDebug) {
const debugArtifacts = await buildProject(projectPath, true, options);
artifacts.push(...debugArtifacts);
}
if (artifacts.length === 0) {
throw new Error('No artifacts were found.');
}
console.log(`Artifacts: ${artifacts}.`);
}

View File

@@ -1,28 +0,0 @@
{
"name": "@tauri-apps/action-cli",
"version": "0.0.10",
"description": "Tauri GitHub Action CLI",
"author": "Lucas Nogueira <lucas@tauri.studio>",
"license": "MIT",
"main": "dist",
"type": "module",
"scripts": {
"build": "rollup --config",
"prepublishOnly": "rollup --config"
},
"repository": {
"type": "git",
"url": "git+https://github.com/tauri-apps/tauri-action.git"
},
"dependencies": {
"@tauri-apps/action-core": "0.2.0",
"minimist": "1.2.7",
"tslib": "2.4.1"
},
"devDependencies": {
"@rollup/plugin-typescript": "10.0.1",
"@types/minimist": "1.2.2",
"rollup": "2.79.1",
"typescript": "4.9.4"
}
}

View File

@@ -1,25 +0,0 @@
import typescript from '@rollup/plugin-typescript';
import pkg from './package.json';
export default {
treeshake: true,
perf: true,
input: { index: 'index.ts' },
output: {
dir: 'dist',
format: 'esm',
entryFileNames: '[name].js',
exports: 'named',
},
plugins: [
typescript({
allowSyntheticDefaultImports: true,
}),
],
external: [...Object.keys(pkg.dependencies || {}), 'path'],
watch: {
chokidar: true,
include: 'src/**',
exclude: 'node_modules/**',
},
};

View File

@@ -1,50 +0,0 @@
# Changelog
## \[0.3.1]
- Added the `bundleIdentifier` input to modify Tauri's default bundle identifier when initializing a new Tauri app.
- [743a37f](https://www.github.com/tauri-apps/tauri-action/commit/743a37fd53cbdd122910b818b9bef7b7aa019134) feat(core): add bundle identifier option ([#263](https://www.github.com/tauri-apps/tauri-action/pull/263)) on 2022-05-11
- Added support to loading version from JSON file in `tauri.conf.json > package > version`.
- [16a8f02](https://www.github.com/tauri-apps/tauri-action/commit/16a8f02ad9b4cff2a0ed6205c7418c36f3e49fd0) build(action): rebuild after fixing version parse error ([#268](https://www.github.com/tauri-apps/tauri-action/pull/268)) on 2022-05-28
## \[0.3.0]
- Added support to JSON5 on `tauri.conf.json[5]`.
- [b9ce5d7](https://www.github.com/tauri-apps/tauri-action/commit/b9ce5d7dc68082d21d30a60103b0ab8c5ddae3a1) feat: add JSON5 support ([#229](https://www.github.com/tauri-apps/tauri-action/pull/229)) on 2022-02-20
- Update to Tauri release candidate.
- [4d70258](https://www.github.com/tauri-apps/tauri-action/commit/4d7025802c5238ef60a62d33ef8c5378637948bb) fix: Change msi naming scheme for recent Tauri upgrades ([#227](https://www.github.com/tauri-apps/tauri-action/pull/227)) on 2022-02-20
- Added support to Cargo workspaces.
- [8e430cc](https://www.github.com/tauri-apps/tauri-action/commit/8e430cc7b0fab28f0a7768f2157933c94f8724f6) feat: cargo workspace support, closes [#196](https://www.github.com/tauri-apps/tauri-action/pull/196) ([#198](https://www.github.com/tauri-apps/tauri-action/pull/198)) on 2021-12-10
## \[0.2.0]
- Removed the `preferGlobal` and `npmScript` inputs and added a `tauriScript` option.
- [a1050c9](https://www.github.com/tauri-apps/tauri-action/commit/a1050c9ec8903fc5c43696da7f07dcfc89475104) refactor: add `tauriScript` input, remove `preferGlobal` and `npmScript` ([#183](https://www.github.com/tauri-apps/tauri-action/pull/183)) on 2021-11-01
## \[0.1.3]
- Linux: Upload AppImage updater artifacts if available.
macOS: Replace `[AppName].app.tgz` to `[AppName].app.tar.gz` to align with updater artifacts.
- [e7266ff](https://www.github.com/tauri-apps/tauri-action/commit/e7266fff1b42c35bfd7ff359d5c6a91ad1308dea) fix(action): Upload AppImage updater artifacts when available ([#163](https://www.github.com/tauri-apps/tauri-action/pull/163)) on 2021-08-31
- Fix incorrect version being used in release names
- [110a0c6](https://www.github.com/tauri-apps/tauri-action/commit/110a0c6da6de9aa85c8e3186ad642650ebc95ab0) Fix version lookup ([#160](https://www.github.com/tauri-apps/tauri-action/pull/160)) on 2021-09-01
## \[0.1.2]
- Adds `args` option to pass arguments to the tauri command.
- [f564b01](https://www.github.com/tauri-apps/tauri-action/commit/f564b01e52fbf240e5e5c12577dd10625fe83580) feat: add `args` option, closes [#131](https://www.github.com/tauri-apps/tauri-action/pull/131) ([#134](https://www.github.com/tauri-apps/tauri-action/pull/134)) on 2021-05-13
- Fixes `execCommand` usage.
- [b4b20f9](https://www.github.com/tauri-apps/tauri-action/commit/b4b20f94709829e5e974255aa8034c78e70bb5d1) fix(core): command execution ([#132](https://www.github.com/tauri-apps/tauri-action/pull/132)) on 2021-05-11
- Include updater artifacts if available.
- [0e9704e](https://www.github.com/tauri-apps/tauri-action/commit/0e9704eb73bcadd1c6acb3a2e9a73a100465db58) Add updater artifacts when available ([#129](https://www.github.com/tauri-apps/tauri-action/pull/129)) on 2021-05-13
## \[0.1.1]
- Fixes `Artifacts not found` error on Linux when the `productName` is converted to `kebab-case`.
- [e6aa180](https://www.github.com/tauri-apps/tauri-action/commit/e6aa1807b6d2c80de70f78fb945e11a659037837) fix(core): product name on Linux is converted to kebab-case ([#125](https://www.github.com/tauri-apps/tauri-action/pull/125)) on 2021-04-29
## \[0.1.0]
- Update to Tauri beta release candidate.
- [b874256](https://github.com/tauri-apps/tauri-action/commit/b87425614119f70be189fddd40a403481b91a328) refactor: rewrite as yarn workspace, add cli as test tool ([#98](https://github.com/tauri-apps/tauri-action/pull/98)) on 2021-04-26
- [dbbc6b4](https://github.com/tauri-apps/tauri-action/commit/dbbc6b4e604ce66a84108e7441ee1b8f38cb82fe) fix(action): test CI and fixes for usage with tauri beta-rc ([#114](https://github.com/tauri-apps/tauri-action/pull/114)) on 2021-04-28

View File

@@ -1,534 +0,0 @@
import { platform } from 'os';
import { readFileSync, existsSync, copyFileSync, writeFileSync } from 'fs';
import { execa } from 'execa';
import { parse as parseToml } from '@iarna/toml';
import { join, resolve, normalize, sep } from 'path';
import { sync as globSync } from 'glob-gitignore';
import ignore from 'ignore';
import JSON5 from 'json5';
export function getPackageJson(root: string): any {
const packageJsonPath = join(root, 'package.json');
if (existsSync(packageJsonPath)) {
const packageJsonString = readFileSync(packageJsonPath).toString();
const packageJson = JSON.parse(packageJsonString);
return packageJson;
}
return null;
}
function getTauriDir(root: string): string | null {
const ignoreRules = ignore();
const gitignorePath = join(root, '.gitignore');
if (existsSync(gitignorePath)) {
ignoreRules.add(readFileSync(gitignorePath).toString());
} else {
ignoreRules.add('node_modules').add('target');
}
const paths = globSync('**/tauri.conf.json', {
cwd: root,
ignore: ignoreRules,
});
const tauriConfPath = paths[0];
return tauriConfPath ? resolve(root, tauriConfPath, '..') : null;
}
function getWorkspaceDir(dir: string): string | null {
const rootPath = dir;
while (dir.length && dir[dir.length - 1] !== sep) {
const manifestPath = join(dir, 'Cargo.toml');
if (existsSync(manifestPath)) {
const toml = parseToml(readFileSync(manifestPath).toString());
// @ts-expect-error
if (toml.workspace?.members) {
// @ts-expect-error
const members: string[] = toml.workspace.members;
if (members.some((m) => resolve(dir, m) === rootPath)) {
return dir;
}
}
}
dir = normalize(join(dir, '..'));
}
return null;
}
function getTargetDir(crateDir: string): string {
const def = join(crateDir, 'target');
if ('CARGO_TARGET_DIR' in process.env) {
return process.env.CARGO_TARGET_DIR ?? def;
}
let dir = crateDir;
while (dir.length && dir[dir.length - 1] !== sep) {
let cargoConfigPath = join(dir, '.cargo/config');
if (!existsSync(cargoConfigPath)) {
cargoConfigPath = join(dir, '.cargo/config.toml');
}
if (existsSync(cargoConfigPath)) {
const cargoConfig = parseToml(readFileSync(cargoConfigPath).toString());
// @ts-ignore
if (cargoConfig.build?.['target-dir']) {
// @ts-ignore
return cargoConfig.build['target-dir'];
}
}
dir = normalize(join(dir, '..'));
}
return def;
}
function hasDependency(dependencyName: string, root: string): boolean {
const packageJson = getPackageJson(root);
return (
packageJson &&
(packageJson.dependencies?.[dependencyName] ||
packageJson.devDependencies?.[dependencyName])
);
}
function usesYarn(root: string): boolean {
return existsSync(join(root, 'yarn.lock'));
}
export function execCommand(
command: string,
args: string[],
{ cwd }: { cwd?: string } = {}
): Promise<void> {
console.log(`running ${command}`, args);
return execa(command, args, {
cwd,
stdio: 'inherit',
env: { FORCE_COLOR: '0' },
}).then();
}
interface CargoManifestBin {
name: string;
}
interface CargoManifest {
package: { version: string; name: string; 'default-run': string };
bin: CargoManifestBin[];
}
interface TauriConfig {
package?: {
productName?: string;
version?: string;
};
tauri?: {
bundle?: {
identifier: string;
windows?: {
wix?: {
language?: string | string[] | { [language: string]: unknown };
};
};
};
};
}
interface Application {
tauriPath: string;
runner: Runner;
name: string;
version: string;
wixLanguage: string | string[] | { [language: string]: unknown };
}
export interface BuildOptions {
configPath: string | null;
distPath: string | null;
iconPath: string | null;
tauriScript: string | null;
args: string[] | null;
bundleIdentifier: string | null;
}
export interface Runner {
runnerCommand: string;
runnerArgs: string[];
}
interface Info {
tauriPath: string | null;
name: string;
version: string;
wixLanguage: string | string[] | { [language: string]: unknown };
}
export interface Artifact {
path: string;
arch: string;
}
function _getJson5Config(contents: string): TauriConfig | null {
try {
const config = JSON5.parse(contents) as TauriConfig;
return config;
} catch (e) {
return null;
}
}
function getConfig(path: string): TauriConfig {
const contents = readFileSync(path).toString();
try {
const config = JSON.parse(contents) as TauriConfig;
return config;
} catch (e) {
let json5Conf = _getJson5Config(contents);
if (json5Conf === null) {
json5Conf = _getJson5Config(
readFileSync(join(path, '..', 'tauri.conf.json5')).toString()
);
}
if (json5Conf) {
return json5Conf;
}
throw e;
}
}
export function getInfo(root: string, inConfigPath: string | null = null): Info {
const tauriDir = getTauriDir(root);
if (tauriDir !== null) {
const configPath = inConfigPath ?? join(tauriDir, 'tauri.conf.json');
let name;
let version;
let wixLanguage: string | string[] | { [language: string]: unknown } =
'en-US';
const config = getConfig(configPath);
if (config.package) {
name = config.package.productName;
version = config.package.version;
if (config.package.version?.endsWith('.json')) {
const packageJsonPath = join(tauriDir, config.package.version);
const contents = readFileSync(packageJsonPath).toString();
version = JSON.parse(contents).version;
}
}
if (!(name && version)) {
const manifestPath = join(tauriDir, 'Cargo.toml');
const cargoManifest = parseToml(
readFileSync(manifestPath).toString()
) as any as CargoManifest;
name = name || cargoManifest.package.name;
version = version || cargoManifest.package.version;
}
if (config.tauri?.bundle?.windows?.wix?.language) {
wixLanguage = config.tauri.bundle.windows.wix.language;
}
if (!(name && version)) {
console.error('Could not determine package name and version');
process.exit(1);
}
return {
tauriPath: tauriDir,
name,
version,
wixLanguage,
};
} else {
const packageJson = getPackageJson(root);
const appName = packageJson
? (packageJson.displayName || packageJson.name).replace(/ /g, '-')
: 'app';
const version = packageJson ? packageJson.version : '0.1.0';
return {
tauriPath: null,
name: appName,
version,
wixLanguage: 'en-US',
};
}
}
export async function buildProject(
root: string,
debug: boolean,
{
configPath,
distPath,
iconPath,
tauriScript,
args,
bundleIdentifier,
}: BuildOptions
): Promise<Artifact[]> {
return new Promise<Runner>((resolve, reject) => {
if (tauriScript) {
const [runnerCommand, ...runnerArgs] = tauriScript.split(' ');
resolve({ runnerCommand, runnerArgs });
} else if (
hasDependency('@tauri-apps/cli', root) ||
hasDependency('vue-cli-plugin-tauri', root)
) {
resolve(
usesYarn(root)
? { runnerCommand: 'yarn', runnerArgs: ['tauri'] }
: { runnerCommand: 'npx', runnerArgs: ['tauri'] }
);
} else {
execCommand('npm', ['install', '-g', '@tauri-apps/cli'], {
cwd: undefined,
})
.then(() => {
resolve({ runnerCommand: 'tauri', runnerArgs: [] });
})
.catch(reject);
}
})
.then((runner: Runner) => {
const info = getInfo(root, configPath);
if (info.tauriPath) {
return {
tauriPath: info.tauriPath,
runner,
name: info.name,
version: info.version,
wixLanguage: info.wixLanguage,
};
} else {
const packageJson = getPackageJson(root);
return execCommand(
runner.runnerCommand,
[...runner.runnerArgs, 'init', '--ci', '--app-name', info.name],
{
cwd: root,
}
).then(() => {
const tauriPath = getTauriDir(root);
if (tauriPath === null) {
console.error('Failed to resolve Tauri path');
process.exit(1);
}
const configPath = join(tauriPath, 'tauri.conf.json');
const config = getConfig(configPath);
console.log(
`Replacing tauri.conf.json config - package.version=${info.version}`
);
const pkgConfig = {
...config.package,
version: info.version,
};
if (packageJson?.productName) {
console.log(
`Replacing tauri.conf.json config - package.productName=${packageJson.productName}`
);
pkgConfig.productName = packageJson.productName;
}
config.package = pkgConfig;
if (bundleIdentifier) {
console.log(
`Replacing tauri.conf.json config - tauri.bundle.identifier=${bundleIdentifier}`
);
config.tauri = {
...config.tauri,
bundle: {
...config.tauri?.bundle,
identifier: bundleIdentifier,
},
};
}
writeFileSync(configPath, JSON.stringify(config, null, 2));
const app = {
tauriPath,
runner,
name: info.name,
version: info.version,
wixLanguage: info.wixLanguage,
};
if (iconPath) {
return execCommand(
runner.runnerCommand,
[...runner.runnerArgs, 'icon', join(root, iconPath)],
{
cwd: root,
}
).then(() => app);
}
return app;
});
}
})
.then((app: Application) => {
const tauriConfPath = join(app.tauriPath, 'tauri.conf.json');
if (configPath !== null) {
copyFileSync(configPath, tauriConfPath);
}
if (distPath) {
const tauriConf = JSON.parse(readFileSync(tauriConfPath).toString());
tauriConf.build.distDir = distPath;
writeFileSync(tauriConfPath, JSON.stringify(tauriConf));
}
const tauriArgs = debug ? ['--debug', ...(args ?? [])] : args ?? [];
let buildCommand;
let buildArgs: string[] = [];
if (hasDependency('vue-cli-plugin-tauri', root)) {
if (usesYarn(root)) {
buildCommand = 'yarn';
buildArgs = ['tauri:build'];
} else {
buildCommand = 'npm';
buildArgs = ['run', 'tauri:build'];
}
} else {
buildCommand = app.runner.runnerCommand;
buildArgs = [...app.runner.runnerArgs, 'build'];
}
return execCommand(buildCommand, [...buildArgs, ...tauriArgs], {
cwd: root,
})
.then(() => {
let fileAppName = app.name;
// on Linux, the app product name is converted to kebab-case
if (!['darwin', 'win32'].includes(platform())) {
fileAppName = fileAppName
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2')
.replace(/[ _.]/g, '-')
.toLowerCase();
}
const cratePath = getWorkspaceDir(app.tauriPath) ?? app.tauriPath;
const found = [...tauriArgs].findIndex(
(e) => e === '-t' || e === '--target'
);
const targetPath = found >= 0 ? [...tauriArgs][found + 1] : '';
const artifactsPath = join(
getTargetDir(cratePath),
targetPath,
debug ? 'debug' : 'release'
);
let arch =
targetPath.search('-') >= 0
? targetPath.split('-')[0]
: process.arch;
if (platform() === 'darwin') {
if (arch === 'x86_64') {
arch = 'x64';
}
return [
join(
artifactsPath,
`bundle/dmg/${fileAppName}_${app.version}_${arch}.dmg`
),
join(artifactsPath, `bundle/macos/${fileAppName}.app`),
join(artifactsPath, `bundle/macos/${fileAppName}.app.tar.gz`),
join(artifactsPath, `bundle/macos/${fileAppName}.app.tar.gz.sig`),
].map((path) => ({ path, arch }));
} else if (platform() === 'win32') {
arch = arch.startsWith('i') ? 'x86' : 'x64';
// If multiple Wix languages are specified, multiple installers (.msi) will be made
// The .zip and .sig are only generated for the first specified language
let langs;
if (typeof app.wixLanguage === 'string') {
langs = [app.wixLanguage];
} else if (Array.isArray(app.wixLanguage)) {
langs = app.wixLanguage;
} else {
langs = Object.keys(app.wixLanguage);
}
const artifacts: string[] = [];
langs.forEach((lang) => {
artifacts.push(
join(
artifactsPath,
`bundle/msi/${fileAppName}_${app.version}_${arch}_${lang}.msi`
)
);
artifacts.push(
join(
artifactsPath,
`bundle/msi/${fileAppName}_${app.version}_${arch}_${lang}.msi.zip`
)
);
artifacts.push(
join(
artifactsPath,
`bundle/msi/${fileAppName}_${app.version}_${arch}_${lang}.msi.zip.sig`
)
);
});
return artifacts.map((path) => ({ path, arch }));
} else {
const debianArch =
arch === 'x64' || arch === 'x86_64'
? 'amd64'
: arch === 'x32' || arch === 'i686'
? 'i386'
: arch === 'arm'
? 'armhf'
: arch === 'aarch64'
? 'arm64'
: arch;
const appImageArch =
arch === 'x64' || arch === 'x86_64'
? 'amd64'
: arch === 'x32' || arch === 'i686'
? 'i386'
: arch;
return [
{
path: join(
artifactsPath,
`bundle/deb/${fileAppName}_${app.version}_${debianArch}.deb`
),
arch: debianArch,
},
{
path: join(
artifactsPath,
`bundle/appimage/${fileAppName}_${app.version}_${appImageArch}.AppImage`
),
arch: appImageArch,
},
{
path: join(
artifactsPath,
`bundle/appimage/${fileAppName}_${app.version}_${appImageArch}.AppImage.tar.gz`
),
arch: appImageArch,
},
{
path: join(
artifactsPath,
`bundle/appimage/${fileAppName}_${app.version}_${appImageArch}.AppImage.tar.gz.sig`
),
arch: appImageArch,
},
];
}
})
.then((artifacts) => {
console.log(
`Expected artifacts paths:\n${artifacts
.map((a) => a.path)
.join('\n')}`
);
return artifacts.filter((p) => existsSync(p.path));
});
});
}

View File

@@ -1,31 +0,0 @@
{
"name": "@tauri-apps/action-core",
"version": "0.3.1",
"author": "Lucas Nogueira <lucas@tauri.studio>",
"license": "MIT",
"main": "dist",
"type": "module",
"scripts": {
"build": "rollup --config",
"prepublishOnly": "rollup --config"
},
"dependencies": {
"@iarna/toml": "2.2.5",
"execa": "6.1.0",
"glob-gitignore": "^1.0.14",
"ignore": "^5.2.0",
"json5": "^2.2.2",
"tslib": "2.4.1"
},
"devDependencies": {
"@rollup/plugin-typescript": "10.0.1",
"@types/node": "16.18.11",
"rollup": "2.79.1",
"typescript": "4.9.4"
},
"engines": {
"node": ">= 12.20.0",
"npm": ">= 6.6.0",
"yarn": ">= 1.19.1"
}
}

View File

@@ -1,25 +0,0 @@
import typescript from '@rollup/plugin-typescript';
import pkg from './package.json';
export default {
treeshake: true,
perf: true,
input: { index: 'index.ts' },
output: {
dir: 'dist',
format: 'esm',
entryFileNames: '[name].js',
exports: 'named',
},
plugins: [
typescript({
tsconfig: './tsconfig.json',
}),
],
external: [...Object.keys(pkg.dependencies || {}), 'path'],
watch: {
chokidar: true,
include: 'src/**',
exclude: 'node_modules/**',
},
};

View File

@@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"declaration": true,
"outDir": "./dist",
"rootDir": ".",
"allowSyntheticDefaultImports": true
},
"include": ["index.ts", "index.d.ts"]
}

2621
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

233
src/build.ts Normal file
View File

@@ -0,0 +1,233 @@
import { platform } from 'os';
import { readFileSync, existsSync, copyFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import { initProject } from './init-project';
import {
execCommand,
getInfo,
getTargetDir,
getWorkspaceDir,
hasDependency,
usesPnpm,
usesYarn,
} from './utils';
import type { Artifact, BuildOptions, Runner } from './types';
async function getRunner(
root: string,
tauriScript: string | null
): Promise<Runner> {
if (tauriScript) {
const [runnerCommand, ...runnerArgs] = tauriScript.split(' ');
return { runnerCommand, runnerArgs };
}
if (
hasDependency('@tauri-apps/cli', root) ||
hasDependency('vue-cli-plugin-tauri', root)
) {
if (usesYarn(root)) return { runnerCommand: 'yarn', runnerArgs: ['tauri'] };
if (usesPnpm(root)) return { runnerCommand: 'pnpm', runnerArgs: ['tauri'] };
// FIXME: This can trigger a download of the tauri alpha package. Likely when the tauri frontend is part of a workspace and projectPath is undefined.
return { runnerCommand: 'npx', runnerArgs: ['tauri'] };
}
await execCommand('npm', ['install', '-g', '@tauri-apps/cli'], {
cwd: undefined,
});
return { runnerCommand: 'tauri', runnerArgs: [] };
}
export async function buildProject(
root: string,
debug: boolean,
buildOpts: BuildOptions
): Promise<Artifact[]> {
const runner = await getRunner(root, buildOpts.tauriScript);
const info = getInfo(root, buildOpts.configPath);
const app = info.tauriPath
? {
tauriPath: info.tauriPath,
runner,
name: info.name,
version: info.version,
wixLanguage: info.wixLanguage,
}
: await initProject(root, runner, info, buildOpts);
const tauriConfPath = join(app.tauriPath, 'tauri.conf.json');
if (buildOpts.configPath !== null) {
copyFileSync(buildOpts.configPath, tauriConfPath);
}
if (buildOpts.distPath) {
const tauriConf = JSON.parse(readFileSync(tauriConfPath).toString());
tauriConf.build.distDir = buildOpts.distPath;
writeFileSync(tauriConfPath, JSON.stringify(tauriConf));
}
const tauriArgs = debug
? ['--debug', ...(buildOpts.args ?? [])]
: buildOpts.args ?? [];
let buildCommand;
let buildArgs: string[] = [];
if (hasDependency('vue-cli-plugin-tauri', root)) {
if (usesYarn(root)) {
buildCommand = 'yarn';
buildArgs = ['tauri:build'];
}
if (usesPnpm(root)) {
buildCommand = 'pnpm';
buildArgs = ['tauri:build'];
} else {
buildCommand = 'npm';
buildArgs = ['run', 'tauri:build'];
}
} else {
buildCommand = app.runner.runnerCommand;
buildArgs = [...app.runner.runnerArgs, 'build'];
}
await execCommand(buildCommand, [...buildArgs, ...tauriArgs], {
cwd: root,
});
let fileAppName = app.name;
// on Linux, the app product name is converted to kebab-case
if (!['darwin', 'win32'].includes(platform())) {
fileAppName = fileAppName
.replace(/([a-z0-9])([A-Z])/g, '$1-$2')
.replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2')
.replace(/[ _.]/g, '-')
.toLowerCase();
}
const cratePath = getWorkspaceDir(app.tauriPath) ?? app.tauriPath;
const found = [...tauriArgs].findIndex((e) => e === '-t' || e === '--target');
const targetPath = found >= 0 ? [...tauriArgs][found + 1] : '';
const artifactsPath = join(
getTargetDir(cratePath),
targetPath,
debug ? 'debug' : 'release'
);
let arch =
targetPath.search('-') >= 0 ? targetPath.split('-')[0] : process.arch;
let artifacts: Artifact[] = [];
if (platform() === 'darwin') {
if (arch === 'x86_64') {
arch = 'x64';
}
artifacts = [
join(
artifactsPath,
`bundle/dmg/${fileAppName}_${app.version}_${arch}.dmg`
),
join(artifactsPath, `bundle/macos/${fileAppName}.app`),
join(artifactsPath, `bundle/macos/${fileAppName}.app.tar.gz`),
join(artifactsPath, `bundle/macos/${fileAppName}.app.tar.gz.sig`),
].map((path) => ({ path, arch }));
} else if (platform() === 'win32') {
arch = arch.startsWith('i') ? 'x86' : 'x64';
// If multiple Wix languages are specified, multiple installers (.msi) will be made
// The .zip and .sig are only generated for the first specified language
let langs;
if (typeof app.wixLanguage === 'string') {
langs = [app.wixLanguage];
} else if (Array.isArray(app.wixLanguage)) {
langs = app.wixLanguage;
} else {
langs = Object.keys(app.wixLanguage);
}
const winArtifacts: string[] = [];
langs.forEach((lang) => {
winArtifacts.push(
join(
artifactsPath,
`bundle/msi/${fileAppName}_${app.version}_${arch}_${lang}.msi`
)
);
winArtifacts.push(
join(
artifactsPath,
`bundle/msi/${fileAppName}_${app.version}_${arch}_${lang}.msi.zip`
)
);
winArtifacts.push(
join(
artifactsPath,
`bundle/msi/${fileAppName}_${app.version}_${arch}_${lang}.msi.zip.sig`
)
);
});
artifacts = winArtifacts.map((path) => ({ path, arch }));
} else {
const debianArch =
arch === 'x64' || arch === 'x86_64'
? 'amd64'
: arch === 'x32' || arch === 'i686'
? 'i386'
: arch === 'arm'
? 'armhf'
: arch === 'aarch64'
? 'arm64'
: arch;
const appImageArch =
arch === 'x64' || arch === 'x86_64'
? 'amd64'
: arch === 'x32' || arch === 'i686'
? 'i386'
: arch;
artifacts = [
{
path: join(
artifactsPath,
`bundle/deb/${fileAppName}_${app.version}_${debianArch}.deb`
),
arch: debianArch,
},
{
path: join(
artifactsPath,
`bundle/appimage/${fileAppName}_${app.version}_${appImageArch}.AppImage`
),
arch: appImageArch,
},
{
path: join(
artifactsPath,
`bundle/appimage/${fileAppName}_${app.version}_${appImageArch}.AppImage.tar.gz`
),
arch: appImageArch,
},
{
path: join(
artifactsPath,
`bundle/appimage/${fileAppName}_${app.version}_${appImageArch}.AppImage.tar.gz.sig`
),
arch: appImageArch,
},
];
}
console.log(
`Expected artifacts paths:\n${artifacts.map((a) => a.path).join('\n')}`
);
return artifacts.filter((p) => existsSync(p.path));
}

View File

@@ -1,7 +1,8 @@
import fs from 'fs';
import * as core from '@actions/core'; import * as core from '@actions/core';
import { getOctokit, context } from '@actions/github'; import { getOctokit, context } from '@actions/github';
import { GitHub } from '@actions/github/lib/utils'; import { GitHub } from '@actions/github/lib/utils';
import fs from 'fs';
interface Release { interface Release {
id: number; id: number;
@@ -25,7 +26,7 @@ function allReleases(
); );
} }
export default async function createRelease( export async function createRelease(
tagName: string, tagName: string,
releaseName: string, releaseName: string,
body?: string, body?: string,
@@ -44,11 +45,12 @@ export default async function createRelease(
const { owner, repo } = context.repo; const { owner, repo } = context.repo;
const bodyPath = core.getInput('body_path', { required: false }); const bodyPath = core.getInput('body_path', { required: false });
let bodyFileContent = null; let bodyFileContent: string | null = null;
if (bodyPath !== '' && !!bodyPath) { if (bodyPath !== '' && !!bodyPath) {
try { try {
bodyFileContent = fs.readFileSync(bodyPath, { encoding: 'utf8' }); bodyFileContent = fs.readFileSync(bodyPath, { encoding: 'utf8' });
} catch (error) { } catch (error) {
//@ts-ignore
core.setFailed(error.message); core.setFailed(error.message);
} }
} }
@@ -60,7 +62,7 @@ export default async function createRelease(
if (draft) { if (draft) {
console.log(`Looking for a draft release with tag ${tagName}...`); console.log(`Looking for a draft release with tag ${tagName}...`);
for await (const response of allReleases(github)) { for await (const response of allReleases(github)) {
let releaseWithTag = response.data.find( const releaseWithTag = response.data.find(
(release) => release.tag_name === tagName (release) => release.tag_name === tagName
); );
if (releaseWithTag) { if (releaseWithTag) {
@@ -84,6 +86,7 @@ export default async function createRelease(
console.log(`Found release with tag ${tagName}.`); console.log(`Found release with tag ${tagName}.`);
} }
} catch (error) { } catch (error) {
// @ts-ignore
if (error.status === 404 || error.message === 'release not found') { if (error.status === 404 || error.message === 'release not found') {
console.log(`Couldn't find release with tag ${tagName}. Creating one.`); console.log(`Couldn't find release with tag ${tagName}. Creating one.`);
const createdRelease = await github.rest.repos.createRelease({ const createdRelease = await github.rest.repos.createRelease({

View File

@@ -1,19 +1,18 @@
import { platform } from 'os';
import * as core from '@actions/core';
import { join, resolve, dirname, basename } from 'path';
import { existsSync } from 'fs'; import { existsSync } from 'fs';
import uploadReleaseAssets from './upload-release-assets'; import { platform } from 'os';
import uploadVersionJSON from './upload-version-json'; import { join, resolve, dirname, basename } from 'path';
import createRelease from './create-release';
import { import * as core from '@actions/core';
getPackageJson,
buildProject,
getInfo,
execCommand,
} from '@tauri-apps/action-core';
import type { BuildOptions } from '@tauri-apps/action-core';
import stringArgv from 'string-argv'; import stringArgv from 'string-argv';
import { createRelease } from './create-release';
import { uploadAssets as uploadReleaseAssets } from './upload-release-assets';
import { uploadVersionJSON } from './upload-version-json';
import { buildProject } from './build';
import { execCommand, getInfo, getPackageJson } from './utils';
import type { BuildOptions } from './types';
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const projectPath = resolve( const projectPath = resolve(
@@ -137,6 +136,7 @@ async function run(): Promise<void> {
}); });
} }
} catch (error) { } catch (error) {
// @ts-ignore
core.setFailed(error.message); core.setFailed(error.message);
} }
} }

81
src/init-project.ts Normal file
View File

@@ -0,0 +1,81 @@
import { writeFileSync } from 'fs';
import { join } from 'path';
import { execCommand, getConfig, getPackageJson, getTauriDir } from './utils';
import type { Application, BuildOptions, Info, Runner } from './types';
export async function initProject(
root: string,
runner: Runner,
info: Info,
{ iconPath, bundleIdentifier }: BuildOptions
): Promise<Application> {
const packageJson = getPackageJson(root);
const tauriPath = getTauriDir(root);
await execCommand(
runner.runnerCommand,
[...runner.runnerArgs, 'init', '--ci', '--app-name', info.name],
{
cwd: root,
}
);
if (tauriPath === null) {
console.error('Failed to resolve Tauri path');
process.exit(1);
}
const configPath = join(tauriPath, 'tauri.conf.json');
const config = getConfig(configPath);
console.log(
`Replacing tauri.conf.json config - package.version=${info.version}`
);
const pkgConfig = {
...config.package,
version: info.version,
};
if (packageJson?.productName) {
console.log(
`Replacing tauri.conf.json config - package.productName=${packageJson.productName}`
);
pkgConfig.productName = packageJson.productName;
}
config.package = pkgConfig;
if (bundleIdentifier) {
console.log(
`Replacing tauri.conf.json config - tauri.bundle.identifier=${bundleIdentifier}`
);
config.tauri = {
...config.tauri,
bundle: {
...config.tauri?.bundle,
identifier: bundleIdentifier,
},
};
}
writeFileSync(configPath, JSON.stringify(config, null, 2));
const app = {
tauriPath,
runner,
name: info.name,
version: info.version,
wixLanguage: info.wixLanguage,
};
if (iconPath) {
await execCommand(
runner.runnerCommand,
[...runner.runnerArgs, 'icon', join(root, iconPath)],
{
cwd: root,
}
);
}
return app;
}

59
src/types.d.ts vendored Normal file
View File

@@ -0,0 +1,59 @@
export interface Application {
tauriPath: string;
runner: Runner;
name: string;
version: string;
wixLanguage: string | string[] | { [language: string]: unknown };
}
export interface Artifact {
path: string;
arch: string;
}
export interface BuildOptions {
configPath: string | null;
distPath: string | null;
iconPath: string | null;
tauriScript: string | null;
args: string[] | null;
bundleIdentifier: string | null;
}
export interface CargoManifestBin {
name: string;
}
export interface CargoManifest {
package: { version: string; name: string; 'default-run': string };
bin: CargoManifestBin[];
}
export interface Info {
tauriPath: string | null;
name: string;
version: string;
wixLanguage: string | string[] | { [language: string]: unknown };
}
export interface Runner {
runnerCommand: string;
runnerArgs: string[];
}
export interface TauriConfig {
package?: {
productName?: string;
version?: string;
};
tauri?: {
bundle?: {
identifier: string;
windows?: {
wix?: {
language?: string | string[] | { [language: string]: unknown };
};
};
};
};
}

View File

@@ -1,12 +1,11 @@
import { getOctokit, context } from '@actions/github';
import { Artifact } from '@tauri-apps/action-core';
import fs from 'fs'; import fs from 'fs';
import { getAssetName } from './utils';
export default async function uploadAssets( import { getOctokit, context } from '@actions/github';
releaseId: number,
assets: Artifact[] import { getAssetName } from './utils';
) { import type { Artifact } from './types';
export async function uploadAssets(releaseId: number, assets: Artifact[]) {
if (process.env.GITHUB_TOKEN === undefined) { if (process.env.GITHUB_TOKEN === undefined) {
throw new Error('GITHUB_TOKEN is required'); throw new Error('GITHUB_TOKEN is required');
} }

View File

@@ -1,13 +1,29 @@
import { getOctokit, context } from '@actions/github';
import { resolve } from 'path';
import { readFileSync, writeFileSync } from 'fs'; import { readFileSync, writeFileSync } from 'fs';
import uploadAssets from './upload-release-assets'; import { platform } from 'os';
import fetch from 'node-fetch'; import { resolve } from 'path';
import { arch, platform } from 'os';
import { getAssetName } from './utils';
import { Artifact } from '@tauri-apps/action-core';
export default async function uploadVersionJSON({ import { getOctokit, context } from '@actions/github';
import { uploadAssets } from './upload-release-assets';
import { getAssetName } from './utils';
import type { Artifact } from './types';
type Platform = {
signature: string;
url: string;
};
type VersionContent = {
version: string;
notes: string;
pub_date: string;
platforms: {
[key: string]: Platform;
};
};
export async function uploadVersionJSON({
version, version,
notes, notes,
tagName, tagName,
@@ -28,7 +44,7 @@ export default async function uploadVersionJSON({
const versionFilename = 'latest.json'; const versionFilename = 'latest.json';
const versionFile = resolve(process.cwd(), versionFilename); const versionFile = resolve(process.cwd(), versionFilename);
const versionContent = { const versionContent: VersionContent = {
version, version,
notes, notes,
pub_date: new Date().toISOString(), pub_date: new Date().toISOString(),
@@ -56,7 +72,7 @@ export default async function uploadVersionJSON({
}, },
} }
) )
).data as any as ArrayBuffer; ).data as unknown as ArrayBuffer;
versionContent.platforms = JSON.parse( versionContent.platforms = JSON.parse(
Buffer.from(assetData).toString() Buffer.from(assetData).toString()
@@ -81,7 +97,7 @@ export default async function uploadVersionJSON({
// Untagged release downloads won't work after the release was published // Untagged release downloads won't work after the release was published
downloadUrl = downloadUrl?.replace( downloadUrl = downloadUrl?.replace(
/\/download\/(untagged-[^\/]+)\//, /\/download\/(untagged-[^/]+)\//,
tagName ? `/download/${tagName}/` : '/latest/download/' tagName ? `/download/${tagName}/` : '/latest/download/'
); );
@@ -104,7 +120,7 @@ export default async function uploadVersionJSON({
: arch; : arch;
// https://github.com/tauri-apps/tauri/blob/fd125f76d768099dc3d4b2d4114349ffc31ffac9/core/tauri/src/updater/core.rs#L856 // https://github.com/tauri-apps/tauri/blob/fd125f76d768099dc3d4b2d4114349ffc31ffac9/core/tauri/src/updater/core.rs#L856
versionContent.platforms[`${os}-${arch}`] = { (versionContent.platforms[`${os}-${arch}`] as unknown) = {
signature: readFileSync(sigFile.path).toString(), signature: readFileSync(sigFile.path).toString(),
url: downloadUrl, url: downloadUrl,
}; };

235
src/utils.ts Normal file
View File

@@ -0,0 +1,235 @@
import { existsSync, readFileSync } from 'fs';
import path, { join, normalize, resolve, sep } from 'path';
import { execa } from 'execa';
import { parse as parseToml } from '@iarna/toml';
import { sync as globSync } from 'glob-gitignore';
import ignore from 'ignore';
import JSON5 from 'json5';
import type { CargoManifest, Info, TauriConfig } from './types';
/*** constants ***/
export const extensions = [
'.app.tar.gz.sig',
'.app.tar.gz',
'.dmg',
'.AppImage.tar.gz.sig',
'.AppImage.tar.gz',
'.AppImage',
'.deb',
'.msi.zip.sig',
'.msi.zip',
'.msi',
];
/*** helper functions ***/
export function getAssetName(assetPath: string) {
const basename = path.basename(assetPath);
const exts = extensions.filter((s) => basename.includes(s));
const ext = exts[0] || path.extname(assetPath);
const filename = basename.replace(ext, '');
let arch = '';
if (ext === '.app.tar.gz.sig' || ext === '.app.tar.gz') {
arch = assetPath.includes('universal-apple-darwin')
? '_universal'
: assetPath.includes('aarch64-apple-darwin')
? '_aarch64'
: '_x64';
}
return assetPath.includes(`${path.sep}debug${path.sep}`)
? `${filename}-debug${arch}${ext}`
: `${filename}${arch}${ext}`;
}
export function getPackageJson(root: string) {
const packageJsonPath = join(root, 'package.json');
if (existsSync(packageJsonPath)) {
const packageJsonString = readFileSync(packageJsonPath).toString();
const packageJson = JSON.parse(packageJsonString);
return packageJson;
}
return null;
}
export function getTauriDir(root: string): string | null {
const ignoreRules = ignore();
const gitignorePath = join(root, '.gitignore');
if (existsSync(gitignorePath)) {
ignoreRules.add(readFileSync(gitignorePath).toString());
} else {
ignoreRules.add('node_modules').add('target');
}
const paths = globSync('**/tauri.conf.json', {
cwd: root,
ignore: ignoreRules,
});
const tauriConfPath = paths[0];
return tauriConfPath ? resolve(root, tauriConfPath, '..') : null;
}
export function getWorkspaceDir(dir: string): string | null {
const rootPath = dir;
while (dir.length && dir[dir.length - 1] !== sep) {
const manifestPath = join(dir, 'Cargo.toml');
if (existsSync(manifestPath)) {
const toml = parseToml(readFileSync(manifestPath).toString());
// @ts-expect-error
if (toml.workspace?.members) {
// @ts-expect-error
const members: string[] = toml.workspace.members;
if (members.some((m) => resolve(dir, m) === rootPath)) {
return dir;
}
}
}
dir = normalize(join(dir, '..'));
}
return null;
}
export function getTargetDir(crateDir: string): string {
const def = join(crateDir, 'target');
if ('CARGO_TARGET_DIR' in process.env) {
return process.env.CARGO_TARGET_DIR ?? def;
}
let dir = crateDir;
while (dir.length && dir[dir.length - 1] !== sep) {
let cargoConfigPath = join(dir, '.cargo/config');
if (!existsSync(cargoConfigPath)) {
cargoConfigPath = join(dir, '.cargo/config.toml');
}
if (existsSync(cargoConfigPath)) {
const cargoConfig = parseToml(readFileSync(cargoConfigPath).toString());
// @ts-ignore
if (cargoConfig.build?.['target-dir']) {
// @ts-ignore
return cargoConfig.build['target-dir'];
}
}
dir = normalize(join(dir, '..'));
}
return def;
}
export function hasDependency(dependencyName: string, root: string): boolean {
const packageJson = getPackageJson(root);
return (
packageJson &&
(packageJson.dependencies?.[dependencyName] ||
packageJson.devDependencies?.[dependencyName])
);
}
export function usesYarn(root: string): boolean {
return existsSync(join(root, 'yarn.lock'));
}
export function usesPnpm(root: string): boolean {
return existsSync(join(root, 'pnpm-lock.yaml'));
}
export function execCommand(
command: string,
args: string[],
{ cwd }: { cwd?: string } = {}
): Promise<void> {
console.log(`running ${command}`, args);
return execa(command, args, {
cwd,
stdio: 'inherit',
env: { FORCE_COLOR: '0' },
}).then();
}
function _getJson5Config(contents: string): TauriConfig | null {
try {
const config = JSON5.parse(contents) as TauriConfig;
return config;
} catch (e) {
return null;
}
}
export function getConfig(path: string): TauriConfig {
const contents = readFileSync(path).toString();
try {
const config = JSON.parse(contents) as TauriConfig;
return config;
} catch (e) {
let json5Conf = _getJson5Config(contents);
if (json5Conf === null) {
json5Conf = _getJson5Config(
readFileSync(join(path, '..', 'tauri.conf.json5')).toString()
);
}
if (json5Conf) {
return json5Conf;
}
throw e;
}
}
export function getInfo(
root: string,
inConfigPath: string | null = null
): Info {
const tauriDir = getTauriDir(root);
if (tauriDir !== null) {
const configPath = inConfigPath ?? join(tauriDir, 'tauri.conf.json');
let name;
let version;
let wixLanguage: string | string[] | { [language: string]: unknown } =
'en-US';
const config = getConfig(configPath);
if (config.package) {
name = config.package.productName;
version = config.package.version;
if (config.package.version?.endsWith('.json')) {
const packageJsonPath = join(tauriDir, config.package.version);
const contents = readFileSync(packageJsonPath).toString();
version = JSON.parse(contents).version;
}
}
if (!(name && version)) {
const manifestPath = join(tauriDir, 'Cargo.toml');
const cargoManifest = parseToml(
readFileSync(manifestPath).toString()
) as unknown as CargoManifest;
name = name || cargoManifest.package.name;
version = version || cargoManifest.package.version;
}
if (config.tauri?.bundle?.windows?.wix?.language) {
wixLanguage = config.tauri.bundle.windows.wix.language;
}
if (!(name && version)) {
console.error('Could not determine package name and version');
process.exit(1);
}
return {
tauriPath: tauriDir,
name,
version,
wixLanguage,
};
} else {
const packageJson = getPackageJson(root);
const appName = packageJson
? (packageJson.displayName || packageJson.name).replace(/ /g, '-')
: 'app';
const version = packageJson ? packageJson.version : '0.1.0';
return {
tauriPath: null,
name: appName,
version,
wixLanguage: 'en-US',
};
}
}

View File

@@ -2,13 +2,12 @@
"compilerOptions": { "compilerOptions": {
"strict": true, "strict": true,
"module": "esnext", "module": "esnext",
"target": "es6", "target": "es2015",
"allowJs": true,
"pretty": true, "pretty": true,
"esModuleInterop": true, "esModuleInterop": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"moduleResolution": "node", "moduleResolution": "node",
"importHelpers": true "importHelpers": true
}, },
"baseUrl": "." "include": ["./src"]
} }

2121
yarn.lock

File diff suppressed because it is too large Load Diff