From 9ee2a5d4a3ce661b94b16205e42d3e56f9fd6654 Mon Sep 17 00:00:00 2001 From: Akkuman Date: Mon, 23 Mar 2026 08:10:02 +0000 Subject: [PATCH 1/4] dev: Add devcontainer config --- .devcontainer/devcontainer.json | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..6c25f3a --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,22 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/javascript-node +{ + "name": "gitea-release-action", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/javascript-node:4-22-bookworm" + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "yarn install", + + // Configure tool-specific properties. + // "customizations": {}, + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} \ No newline at end of file From fd3cba014c29a5b38cc0f233ca165e65040cb257 Mon Sep 17 00:00:00 2001 From: Akkuman Date: Mon, 23 Mar 2026 08:12:30 +0000 Subject: [PATCH 2/4] fix: use readStream instead of read ralated: https://github.com/akkuman/gitea-release-action/issues/10 --- dist/index.js | 101 +++++++++++++++++++++++++++++++++++++++++++------- main.js | 91 ++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 168 insertions(+), 24 deletions(-) diff --git a/dist/index.js b/dist/index.js index f4be3fd..25b43a3 100644 --- a/dist/index.js +++ b/dist/index.js @@ -40594,6 +40594,8 @@ var __webpack_exports__ = {}; // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk. (() => { +;// CONCATENATED MODULE: external "node:fs/promises" +const promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:fs/promises"); // EXTERNAL MODULE: external "fs" var external_fs_ = __nccwpck_require__(7147); var external_fs_namespaceObject = /*#__PURE__*/__nccwpck_require__.t(external_fs_, 2); @@ -43826,7 +43828,7 @@ var external_path_ = __nccwpck_require__(1017); // EXTERNAL MODULE: external "url" var external_url_ = __nccwpck_require__(7310); ;// CONCATENATED MODULE: external "fs/promises" -const promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs/promises"); +const external_fs_promises_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs/promises"); // EXTERNAL MODULE: external "events" var external_events_ = __nccwpck_require__(2361); // EXTERNAL MODULE: external "stream" @@ -44870,10 +44872,10 @@ const defaultFS = { readlinkSync: external_fs_.readlinkSync, realpathSync, promises: { - lstat: promises_namespaceObject.lstat, - readdir: promises_namespaceObject.readdir, - readlink: promises_namespaceObject.readlink, - realpath: promises_namespaceObject.realpath, + lstat: external_fs_promises_namespaceObject.lstat, + readdir: external_fs_promises_namespaceObject.readdir, + readlink: external_fs_promises_namespaceObject.readlink, + realpath: external_fs_promises_namespaceObject.realpath, }, }; // if they just gave us require('fs') then use our default @@ -48144,6 +48146,7 @@ var crypto_js = __nccwpck_require__(4134); + function getIsTrue(v) { const trueValue = ['true', 'True', 'TRUE'] return trueValue.includes(v) @@ -48262,6 +48265,67 @@ function paths(patterns) { }, []); }; +async function createStreamableFile(fpath) { + const name = external_path_.basename(fpath); + const handle = await promises_namespaceObject.open(fpath); + const { size } = await handle.stat(); + + const file = new external_buffer_.File([], name); + file.stream = () => handle.readableWebStream(); + + // Set correct size otherwise, fetch will encounter UND_ERR_REQ_CONTENT_LENGTH_MISMATCH + Object.defineProperty(file, 'size', { get: () => size }); + + return file; +} + + +async function calculateMultipleHashes(file, algorithms = ['md5', 'sha256']) { + const stream = file.stream(); + const reader = stream.getReader(); + + const hashers = algorithms.map(alg => { + switch(alg.toLowerCase()) { + case 'md5': + return { name: 'md5', instance: crypto_js.algo.MD5.create() }; + case 'sha1': + return { name: 'sha1', instance: crypto_js.algo.SHA1.create() }; + case 'sha256': + return { name: 'sha256', instance: crypto_js.algo.SHA256.create() }; + case 'sha512': + return { name: 'sha512', instance: crypto_js.algo.SHA512.create() }; + default: + throw new Error(`not support hash: ${alg}`); + } + }); + + try { + while (true) { + const { done, value } = await reader.read(); + + if (done) { + break; + } + + const wordArray = crypto_js.lib.WordArray.create(value); + + hashers.forEach(hasher => { + hasher.instance.update(wordArray); + }); + } + + const result = {}; + hashers.forEach(hasher => { + result[hasher.name] = hasher.instance.finalize().toString(crypto_js.enc.Hex); + }); + + return result; + } finally { + reader.releaseLock(); + } +} + + /** * * @param {gitea.GiteaApi} client @@ -48302,19 +48366,29 @@ async function uploadFiles(client, owner, repo, release_id, all_files, params) { } // upload new release attachment for (const filepath of all_files) { - const content = external_fs_.readFileSync(filepath); - let blob = new external_buffer_.Blob([content]); + let curfile = await createStreamableFile(filepath) await client.repository.repoCreateReleaseAttachment({ owner: owner, repo: repo, id: release_id, - attachment: blob, + attachment: curfile, name: external_path_.basename(filepath), }) + let algorithms = []; if (params.md5sum) { - let wordArray = crypto_js.lib.WordArray.create(content); - let hash = crypto_js.MD5(wordArray).toString(); - blob = new external_buffer_.Blob([hash], { type : 'plain/text' }); + algorithms = algorithms.concat('md5'); + } + if (params.sha256sum) { + algorithms = algorithms.concat('sha256'); + } + let hashes = {}; + if (algorithms.length !== 0) { + curfile = await createStreamableFile(filepath) + hashes = await calculateMultipleHashes(curfile, algorithms) + } + if (params.md5sum) { + let hash = hashes.md5; + let blob = new external_buffer_.Blob([hash], { type : 'plain/text' }); await client.repository.repoCreateReleaseAttachment({ owner: owner, repo: repo, @@ -48324,9 +48398,8 @@ async function uploadFiles(client, owner, repo, release_id, all_files, params) { }) } if (params.sha256sum) { - let wordArray = crypto_js.lib.WordArray.create(content); - let hash = crypto_js.SHA256(wordArray).toString(); - blob = new external_buffer_.Blob([hash], { type : 'plain/text' }); + let hash = hashes.sha256; + let blob = new external_buffer_.Blob([hash], { type : 'plain/text' }); await client.repository.repoCreateReleaseAttachment({ owner: owner, repo: repo, diff --git a/main.js b/main.js index 4f5f42c..f93d799 100644 --- a/main.js +++ b/main.js @@ -1,5 +1,6 @@ +import asyncfs from "node:fs/promises"; import fs from "fs"; -import { Blob } from "buffer"; +import { Blob, File } from "buffer"; import * as glob from "glob"; import core from "@actions/core"; @@ -126,6 +127,67 @@ function paths(patterns) { }, []); }; +async function createStreamableFile(fpath) { + const name = path.basename(fpath); + const handle = await asyncfs.open(fpath); + const { size } = await handle.stat(); + + const file = new File([], name); + file.stream = () => handle.readableWebStream(); + + // Set correct size otherwise, fetch will encounter UND_ERR_REQ_CONTENT_LENGTH_MISMATCH + Object.defineProperty(file, 'size', { get: () => size }); + + return file; +} + + +async function calculateMultipleHashes(file, algorithms = ['md5', 'sha256']) { + const stream = file.stream(); + const reader = stream.getReader(); + + const hashers = algorithms.map(alg => { + switch(alg.toLowerCase()) { + case 'md5': + return { name: 'md5', instance: CryptoJS.algo.MD5.create() }; + case 'sha1': + return { name: 'sha1', instance: CryptoJS.algo.SHA1.create() }; + case 'sha256': + return { name: 'sha256', instance: CryptoJS.algo.SHA256.create() }; + case 'sha512': + return { name: 'sha512', instance: CryptoJS.algo.SHA512.create() }; + default: + throw new Error(`not support hash: ${alg}`); + } + }); + + try { + while (true) { + const { done, value } = await reader.read(); + + if (done) { + break; + } + + const wordArray = CryptoJS.lib.WordArray.create(value); + + hashers.forEach(hasher => { + hasher.instance.update(wordArray); + }); + } + + const result = {}; + hashers.forEach(hasher => { + result[hasher.name] = hasher.instance.finalize().toString(CryptoJS.enc.Hex); + }); + + return result; + } finally { + reader.releaseLock(); + } +} + + /** * * @param {gitea.GiteaApi} client @@ -166,19 +228,29 @@ async function uploadFiles(client, owner, repo, release_id, all_files, params) { } // upload new release attachment for (const filepath of all_files) { - const content = fs.readFileSync(filepath); - let blob = new Blob([content]); + let curfile = await createStreamableFile(filepath) await client.repository.repoCreateReleaseAttachment({ owner: owner, repo: repo, id: release_id, - attachment: blob, + attachment: curfile, name: path.basename(filepath), }) + let algorithms = []; if (params.md5sum) { - let wordArray = CryptoJS.lib.WordArray.create(content); - let hash = CryptoJS.MD5(wordArray).toString(); - blob = new Blob([hash], { type : 'plain/text' }); + algorithms = algorithms.concat('md5'); + } + if (params.sha256sum) { + algorithms = algorithms.concat('sha256'); + } + let hashes = {}; + if (algorithms.length !== 0) { + curfile = await createStreamableFile(filepath) + hashes = await calculateMultipleHashes(curfile, algorithms) + } + if (params.md5sum) { + let hash = hashes.md5; + let blob = new Blob([hash], { type : 'plain/text' }); await client.repository.repoCreateReleaseAttachment({ owner: owner, repo: repo, @@ -188,9 +260,8 @@ async function uploadFiles(client, owner, repo, release_id, all_files, params) { }) } if (params.sha256sum) { - let wordArray = CryptoJS.lib.WordArray.create(content); - let hash = CryptoJS.SHA256(wordArray).toString(); - blob = new Blob([hash], { type : 'plain/text' }); + let hash = hashes.sha256; + let blob = new Blob([hash], { type : 'plain/text' }); await client.repository.repoCreateReleaseAttachment({ owner: owner, repo: repo, From e476391eeee0cac64d4158e110548b6ae7d8bbe7 Mon Sep 17 00:00:00 2001 From: Akkuman Date: Tue, 24 Mar 2026 05:37:37 +0000 Subject: [PATCH 3/4] fix: autoclose stream --- dist/index.js | 16 ++++++++-------- main.js | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/dist/index.js b/dist/index.js index 25b43a3..6e476e7 100644 --- a/dist/index.js +++ b/dist/index.js @@ -48266,17 +48266,17 @@ function paths(patterns) { }; async function createStreamableFile(fpath) { - const name = external_path_.basename(fpath); - const handle = await promises_namespaceObject.open(fpath); - const { size } = await handle.stat(); + const name = external_path_.basename(fpath); + const handle = await promises_namespaceObject.open(fpath); + const { size } = await handle.stat(); - const file = new external_buffer_.File([], name); - file.stream = () => handle.readableWebStream(); + const file = new external_buffer_.File([], name); + file.stream = () => handle.readableWebStream({autoClose: true}); - // Set correct size otherwise, fetch will encounter UND_ERR_REQ_CONTENT_LENGTH_MISMATCH - Object.defineProperty(file, 'size', { get: () => size }); + // Set correct size otherwise, fetch will encounter UND_ERR_REQ_CONTENT_LENGTH_MISMATCH + Object.defineProperty(file, 'size', { get: () => size }); - return file; + return file; } diff --git a/main.js b/main.js index f93d799..07d23fb 100644 --- a/main.js +++ b/main.js @@ -128,17 +128,17 @@ function paths(patterns) { }; async function createStreamableFile(fpath) { - const name = path.basename(fpath); - const handle = await asyncfs.open(fpath); - const { size } = await handle.stat(); + const name = path.basename(fpath); + const handle = await asyncfs.open(fpath); + const { size } = await handle.stat(); - const file = new File([], name); - file.stream = () => handle.readableWebStream(); + const file = new File([], name); + file.stream = () => handle.readableWebStream({autoClose: true}); - // Set correct size otherwise, fetch will encounter UND_ERR_REQ_CONTENT_LENGTH_MISMATCH - Object.defineProperty(file, 'size', { get: () => size }); + // Set correct size otherwise, fetch will encounter UND_ERR_REQ_CONTENT_LENGTH_MISMATCH + Object.defineProperty(file, 'size', { get: () => size }); - return file; + return file; } From b8d9144f302c68610911db1aaf722708d5c02d94 Mon Sep 17 00:00:00 2001 From: Akkuman Date: Tue, 24 Mar 2026 05:55:35 +0000 Subject: [PATCH 4/4] fix: must still call the`fileHandle.close()` method since nodejs v17 --- dist/index.js | 5 ++++- main.js | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index 6e476e7..93037c5 100644 --- a/dist/index.js +++ b/dist/index.js @@ -48271,7 +48271,8 @@ async function createStreamableFile(fpath) { const { size } = await handle.stat(); const file = new external_buffer_.File([], name); - file.stream = () => handle.readableWebStream({autoClose: true}); + file.stream = () => handle.readableWebStream(); + file.close = async () => await handle?.close(); // Set correct size otherwise, fetch will encounter UND_ERR_REQ_CONTENT_LENGTH_MISMATCH Object.defineProperty(file, 'size', { get: () => size }); @@ -48374,6 +48375,7 @@ async function uploadFiles(client, owner, repo, release_id, all_files, params) { attachment: curfile, name: external_path_.basename(filepath), }) + await curfile.close(); let algorithms = []; if (params.md5sum) { algorithms = algorithms.concat('md5'); @@ -48385,6 +48387,7 @@ async function uploadFiles(client, owner, repo, release_id, all_files, params) { if (algorithms.length !== 0) { curfile = await createStreamableFile(filepath) hashes = await calculateMultipleHashes(curfile, algorithms) + await curfile.close(); } if (params.md5sum) { let hash = hashes.md5; diff --git a/main.js b/main.js index 07d23fb..3ad37f5 100644 --- a/main.js +++ b/main.js @@ -133,7 +133,8 @@ async function createStreamableFile(fpath) { const { size } = await handle.stat(); const file = new File([], name); - file.stream = () => handle.readableWebStream({autoClose: true}); + file.stream = () => handle.readableWebStream(); + file.close = async () => await handle?.close(); // Set correct size otherwise, fetch will encounter UND_ERR_REQ_CONTENT_LENGTH_MISMATCH Object.defineProperty(file, 'size', { get: () => size }); @@ -236,6 +237,7 @@ async function uploadFiles(client, owner, repo, release_id, all_files, params) { attachment: curfile, name: path.basename(filepath), }) + await curfile.close(); let algorithms = []; if (params.md5sum) { algorithms = algorithms.concat('md5'); @@ -247,6 +249,7 @@ async function uploadFiles(client, owner, repo, release_id, all_files, params) { if (algorithms.length !== 0) { curfile = await createStreamableFile(filepath) hashes = await calculateMultipleHashes(curfile, algorithms) + await curfile.close(); } if (params.md5sum) { let hash = hashes.md5;