From 1ac046142c5ce56e63c83a049851684d6c15e53a Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 11 Aug 2024 16:47:01 +0900 Subject: [PATCH 01/11] add containsEntryInManifest --- src/utils/walk.ts | 39 +++ src/utils/walk_test.ts | 241 +++++++++++++++++- src/utils/walk_testdata/single_file/a.txt | 1 + src/utils/walk_testdata/two_levels/a.txt | 1 + .../walk_testdata/two_levels/inner/b.txt | 1 + 5 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 src/utils/walk_testdata/single_file/a.txt create mode 100644 src/utils/walk_testdata/two_levels/a.txt create mode 100644 src/utils/walk_testdata/two_levels/inner/b.txt diff --git a/src/utils/walk.ts b/src/utils/walk.ts index 5bfc458a..da39fe0e 100644 --- a/src/utils/walk.ts +++ b/src/utils/walk.ts @@ -1,4 +1,5 @@ import { globToRegExp, isGlob, join, normalize } from "../../deps.ts"; +import { unreachable } from "../../tests/deps.ts"; import type { ManifestEntry } from "./api_types.ts"; /** Calculate git object hash, like `git hash-object` does. */ @@ -98,3 +99,41 @@ export function convertPatternToRegExp(pattern: string): RegExp { ? new RegExp(globToRegExp(normalize(pattern)).toString().slice(1, -2)) : new RegExp(`^${normalize(pattern)}`); } + +/** + * Determines if the manifest contains the entry at the given relative path. + * + * @param manifestEntries manifest entries to search + * @param entryRelativePathToLookup a relative path to look up in the manifest + * @returns `true` if the manifest contains the entry at the given relative path + */ +export function containsEntryInManifest( + manifestEntries: Record, + entryRelativePathToLookup: string, +): boolean { + for (const [entryName, entry] of Object.entries(manifestEntries)) { + switch (entry.kind) { + case "file": + case "symlink": { + if (entryName === entryRelativePathToLookup) { + return true; + } + break; + } + case "directory": { + if (!entryRelativePathToLookup.startsWith(entryName)) { + break; + } + + const relativePath = entryRelativePathToLookup.slice( + entryName.length + 1, + ); + return containsEntryInManifest(entry.entries, relativePath); + } + default: + unreachable(); + } + } + + return false; +} diff --git a/src/utils/walk_test.ts b/src/utils/walk_test.ts index 827af2df..3bf05588 100644 --- a/src/utils/walk_test.ts +++ b/src/utils/walk_test.ts @@ -1,5 +1,11 @@ -import { assertEquals } from "../../tests/deps.ts"; -import { convertPatternToRegExp } from "./walk.ts"; +import { dirname, fromFileUrl, join } from "../../deps.ts"; +import { assert, assertEquals, assertFalse } from "../../tests/deps.ts"; +import type { ManifestEntry } from "./api_types.ts"; +import { + containsEntryInManifest, + convertPatternToRegExp, + walk, +} from "./walk.ts"; Deno.test({ name: "convertPatternToRegExp", @@ -10,3 +16,234 @@ Deno.test({ assertEquals(convertPatternToRegExp("*.ts"), new RegExp("^[^/]*\\.ts/*")); }, }); + +Deno.test({ + name: "walk and containsEntryInManifest", + fn: async (t) => { + type Test = { + name: string; + input: { + testdir: string; + include: readonly string[]; + exclude: readonly string[]; + }; + expected: { + entries: Record; + containedEntries: readonly string[]; + notContainedEntries: readonly string[]; + }; + }; + + const tests: Test[] = [ + { + name: "single_file", + input: { + testdir: "single_file", + include: [], + exclude: [], + }, + expected: { + entries: { + "a.txt": { + kind: "file", + gitSha1: "78981922613b2afb6025042ff6bd878ac1994e85", + size: 2, + }, + }, + containedEntries: ["a.txt"], + notContainedEntries: ["b.txt", ".git", "deno.json"], + }, + }, + { + name: "single_file with include", + input: { + testdir: "single_file", + include: ["a.txt"], + exclude: [], + }, + expected: { + entries: { + "a.txt": { + kind: "file", + gitSha1: "78981922613b2afb6025042ff6bd878ac1994e85", + size: 2, + }, + }, + containedEntries: ["a.txt"], + notContainedEntries: ["b.txt", ".git", "deno.json"], + }, + }, + { + name: "single_file with include 2", + input: { + testdir: "single_file", + include: ["*.txt"], + exclude: [], + }, + expected: { + entries: { + "a.txt": { + kind: "file", + gitSha1: "78981922613b2afb6025042ff6bd878ac1994e85", + size: 2, + }, + }, + containedEntries: ["a.txt"], + notContainedEntries: ["b.txt", ".git", "deno.json"], + }, + }, + { + name: "single_file with exclude", + input: { + testdir: "single_file", + include: [], + exclude: ["a.txt"], + }, + expected: { + entries: {}, + containedEntries: [], + notContainedEntries: ["a.txt", "b.txt", ".git", "deno.json"], + }, + }, + { + name: "two_levels", + input: { + testdir: "two_levels", + include: [], + exclude: [], + }, + expected: { + entries: { + "a.txt": { + kind: "file", + gitSha1: "78981922613b2afb6025042ff6bd878ac1994e85", + size: 2, + }, + "inner": { + kind: "directory", + entries: { + "b.txt": { + kind: "file", + gitSha1: "61780798228d17af2d34fce4cfbdf35556832472", + size: 2, + }, + }, + }, + }, + containedEntries: ["a.txt", "inner/b.txt"], + notContainedEntries: [ + "b.txt", + "inner/a.txt", + ".git", + "deno.json", + "inner", + ], + }, + }, + { + name: "two_levels with include", + input: { + testdir: "two_levels", + include: ["**/b.txt"], + exclude: [], + }, + expected: { + entries: { + "inner": { + kind: "directory", + entries: { + "b.txt": { + kind: "file", + gitSha1: "61780798228d17af2d34fce4cfbdf35556832472", + size: 2, + }, + }, + }, + }, + containedEntries: ["inner/b.txt"], + notContainedEntries: [ + "a.txt", + "b.txt", + "inner/a.txt", + ".git", + "deno.json", + "inner", + ], + }, + }, + { + name: "two_levels with exclude", + input: { + testdir: "two_levels", + include: [], + exclude: ["*.txt"], + }, + expected: { + entries: { + "inner": { + kind: "directory", + entries: { + "b.txt": { + kind: "file", + gitSha1: "61780798228d17af2d34fce4cfbdf35556832472", + size: 2, + }, + }, + }, + }, + containedEntries: ["inner/b.txt"], + notContainedEntries: [ + "a.txt", + "b.txt", + "inner/a.txt", + ".git", + "deno.json", + "inner", + ], + }, + }, + ]; + + for (const test of tests) { + await t.step({ + name: test.name, + fn: async () => { + const entries = await walk( + join( + fromFileUrl(dirname(import.meta.url)), + "walk_testdata", + test.input.testdir, + ), + join( + fromFileUrl(dirname(import.meta.url)), + "walk_testdata", + test.input.testdir, + ), + new Map(), + { + include: test.input.include.map(convertPatternToRegExp), + exclude: test.input.exclude.map(convertPatternToRegExp), + }, + ); + assertEquals(entries, test.expected.entries); + + for (const entry of test.expected.containedEntries) { + const contained = containsEntryInManifest(entries, entry); + assert( + contained, + `Expected ${entry} to be contained in the manifest`, + ); + } + + for (const entry of test.expected.notContainedEntries) { + const contained = containsEntryInManifest(entries, entry); + assertFalse( + contained, + `Expected ${entry} to *not* be contained in the manifest`, + ); + } + }, + }); + } + }, +}); diff --git a/src/utils/walk_testdata/single_file/a.txt b/src/utils/walk_testdata/single_file/a.txt new file mode 100644 index 00000000..78981922 --- /dev/null +++ b/src/utils/walk_testdata/single_file/a.txt @@ -0,0 +1 @@ +a diff --git a/src/utils/walk_testdata/two_levels/a.txt b/src/utils/walk_testdata/two_levels/a.txt new file mode 100644 index 00000000..78981922 --- /dev/null +++ b/src/utils/walk_testdata/two_levels/a.txt @@ -0,0 +1 @@ +a diff --git a/src/utils/walk_testdata/two_levels/inner/b.txt b/src/utils/walk_testdata/two_levels/inner/b.txt new file mode 100644 index 00000000..61780798 --- /dev/null +++ b/src/utils/walk_testdata/two_levels/inner/b.txt @@ -0,0 +1 @@ +b From e6cc4e299f5bad2794c41091881c29b111e50757 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 11 Aug 2024 16:48:16 +0900 Subject: [PATCH 02/11] rename walk.ts -> manifest.ts --- src/utils/{walk.ts => manifest.ts} | 0 src/utils/{walk_test.ts => manifest_test.ts} | 6 +++--- .../{walk_testdata => manifest_testdata}/single_file/a.txt | 0 .../{walk_testdata => manifest_testdata}/two_levels/a.txt | 0 .../two_levels/inner/b.txt | 0 5 files changed, 3 insertions(+), 3 deletions(-) rename src/utils/{walk.ts => manifest.ts} (100%) rename src/utils/{walk_test.ts => manifest_test.ts} (98%) rename src/utils/{walk_testdata => manifest_testdata}/single_file/a.txt (100%) rename src/utils/{walk_testdata => manifest_testdata}/two_levels/a.txt (100%) rename src/utils/{walk_testdata => manifest_testdata}/two_levels/inner/b.txt (100%) diff --git a/src/utils/walk.ts b/src/utils/manifest.ts similarity index 100% rename from src/utils/walk.ts rename to src/utils/manifest.ts diff --git a/src/utils/walk_test.ts b/src/utils/manifest_test.ts similarity index 98% rename from src/utils/walk_test.ts rename to src/utils/manifest_test.ts index 3bf05588..40bc2de7 100644 --- a/src/utils/walk_test.ts +++ b/src/utils/manifest_test.ts @@ -5,7 +5,7 @@ import { containsEntryInManifest, convertPatternToRegExp, walk, -} from "./walk.ts"; +} from "./manifest.ts"; Deno.test({ name: "convertPatternToRegExp", @@ -211,12 +211,12 @@ Deno.test({ const entries = await walk( join( fromFileUrl(dirname(import.meta.url)), - "walk_testdata", + "manifest_testdata", test.input.testdir, ), join( fromFileUrl(dirname(import.meta.url)), - "walk_testdata", + "manifest_testdata", test.input.testdir, ), new Map(), diff --git a/src/utils/walk_testdata/single_file/a.txt b/src/utils/manifest_testdata/single_file/a.txt similarity index 100% rename from src/utils/walk_testdata/single_file/a.txt rename to src/utils/manifest_testdata/single_file/a.txt diff --git a/src/utils/walk_testdata/two_levels/a.txt b/src/utils/manifest_testdata/two_levels/a.txt similarity index 100% rename from src/utils/walk_testdata/two_levels/a.txt rename to src/utils/manifest_testdata/two_levels/a.txt diff --git a/src/utils/walk_testdata/two_levels/inner/b.txt b/src/utils/manifest_testdata/two_levels/inner/b.txt similarity index 100% rename from src/utils/walk_testdata/two_levels/inner/b.txt rename to src/utils/manifest_testdata/two_levels/inner/b.txt From 75a495525631ef5d9735481bdac691e95b66cc60 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 11 Aug 2024 16:55:59 +0900 Subject: [PATCH 03/11] refactor: make recursive part of walk function private --- src/subcommands/deploy.ts | 9 ++++++--- src/utils/manifest.ts | 24 +++++++++++++++++++++--- src/utils/manifest_test.ts | 9 ++++----- 3 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/subcommands/deploy.ts b/src/subcommands/deploy.ts index 7bb6659b..23bc10a5 100644 --- a/src/subcommands/deploy.ts +++ b/src/subcommands/deploy.ts @@ -8,7 +8,7 @@ import { error } from "../error.ts"; import { API, APIError, endpoint } from "../utils/api.ts"; import type { ManifestEntry } from "../utils/api_types.ts"; import { parseEntrypoint } from "../utils/entrypoint.ts"; -import { convertPatternToRegExp, walk } from "../utils/walk.ts"; +import { convertPatternToRegExp, walk } from "../utils/manifest.ts"; import TokenProvisioner from "../utils/access_token.ts"; import type { Args as RawArgs } from "../args.ts"; import organization from "../utils/organization.ts"; @@ -273,10 +273,13 @@ async function deploy(opts: DeployOpts): Promise { if (opts.static) { wait("").start().info(`Uploading all files from the current dir (${cwd})`); const assetSpinner = wait("Finding static assets...").start(); - const assets = new Map(); const include = opts.include.map(convertPatternToRegExp); const exclude = opts.exclude.map(convertPatternToRegExp); - const entries = await walk(cwd, cwd, assets, { include, exclude }); + const { manifestEntries: entries, hashPathMap: assets } = await walk( + cwd, + cwd, + { include, exclude }, + ); assetSpinner.succeed( `Found ${assets.size} asset${assets.size === 1 ? "" : "s"}.`, ); diff --git a/src/utils/manifest.ts b/src/utils/manifest.ts index da39fe0e..5ce45f3d 100644 --- a/src/utils/manifest.ts +++ b/src/utils/manifest.ts @@ -39,7 +39,25 @@ function include( export async function walk( cwd: string, dir: string, - files: Map, + options: { include: RegExp[]; exclude: RegExp[] }, +): Promise< + { + manifestEntries: Record; + hashPathMap: Map; + } +> { + const hashPathMap = new Map(); + const manifestEntries = await walkInner(cwd, dir, hashPathMap, options); + return { + manifestEntries, + hashPathMap, + }; +} + +async function walkInner( + cwd: string, + dir: string, + hashPathMap: Map, options: { include: RegExp[]; exclude: RegExp[] }, ): Promise> { const entries: Record = {}; @@ -66,12 +84,12 @@ export async function walk( gitSha1, size: data.byteLength, }; - files.set(gitSha1, path); + hashPathMap.set(gitSha1, path); } else if (file.isDirectory) { if (relative === "/.git") continue; entry = { kind: "directory", - entries: await walk(cwd, path, files, options), + entries: await walkInner(cwd, path, hashPathMap, options), }; } else if (file.isSymlink) { const target = await Deno.readLink(path); diff --git a/src/utils/manifest_test.ts b/src/utils/manifest_test.ts index 40bc2de7..14c1c3d5 100644 --- a/src/utils/manifest_test.ts +++ b/src/utils/manifest_test.ts @@ -208,7 +208,7 @@ Deno.test({ await t.step({ name: test.name, fn: async () => { - const entries = await walk( + const { manifestEntries } = await walk( join( fromFileUrl(dirname(import.meta.url)), "manifest_testdata", @@ -219,16 +219,15 @@ Deno.test({ "manifest_testdata", test.input.testdir, ), - new Map(), { include: test.input.include.map(convertPatternToRegExp), exclude: test.input.exclude.map(convertPatternToRegExp), }, ); - assertEquals(entries, test.expected.entries); + assertEquals(manifestEntries, test.expected.entries); for (const entry of test.expected.containedEntries) { - const contained = containsEntryInManifest(entries, entry); + const contained = containsEntryInManifest(manifestEntries, entry); assert( contained, `Expected ${entry} to be contained in the manifest`, @@ -236,7 +235,7 @@ Deno.test({ } for (const entry of test.expected.notContainedEntries) { - const contained = containsEntryInManifest(entries, entry); + const contained = containsEntryInManifest(manifestEntries, entry); assertFalse( contained, `Expected ${entry} to *not* be contained in the manifest`, From 598125ae11959ee6a5cef29cb7e5558425c59320 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 11 Aug 2024 17:30:05 +0900 Subject: [PATCH 04/11] exit or show warning when import map will not be applied --- src/subcommands/deploy.ts | 35 +++++++++++++++++++++++++++++++++-- src/utils/mod.ts | 2 +- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/subcommands/deploy.ts b/src/subcommands/deploy.ts index 23bc10a5..623af02f 100644 --- a/src/subcommands/deploy.ts +++ b/src/subcommands/deploy.ts @@ -1,6 +1,6 @@ // Copyright 2021 Deno Land Inc. All rights reserved. MIT license. -import { fromFileUrl, type Spinner } from "../../deps.ts"; +import { fromFileUrl, relative, type Spinner } from "../../deps.ts"; import { envVarsFromArgs } from "../utils/env_vars.ts"; import { wait } from "../utils/spinner.ts"; import configFile from "../config_file.ts"; @@ -8,7 +8,11 @@ import { error } from "../error.ts"; import { API, APIError, endpoint } from "../utils/api.ts"; import type { ManifestEntry } from "../utils/api_types.ts"; import { parseEntrypoint } from "../utils/entrypoint.ts"; -import { convertPatternToRegExp, walk } from "../utils/manifest.ts"; +import { + containsEntryInManifest, + convertPatternToRegExp, + walk, +} from "../utils/manifest.ts"; import TokenProvisioner from "../utils/access_token.ts"; import type { Args as RawArgs } from "../args.ts"; import organization from "../utils/organization.ts"; @@ -284,6 +288,33 @@ async function deploy(opts: DeployOpts): Promise { `Found ${assets.size} asset${assets.size === 1 ? "" : "s"}.`, ); + // If the import map is specified but not in the manifest, error out. + if ( + opts.importMapUrl !== null && + !containsEntryInManifest( + entries, + relative(cwd, fromFileUrl(opts.importMapUrl)), + ) + ) { + error( + `Import map ${opts.importMapUrl} not found in the assets to be uploaded. Please check --include and --exclude options to make sure the import map is included.`, + ); + } + + // If the config file is present but not in the manifest, show a warning + // that any import map settings in the config file will not be used. + if ( + opts.importMapUrl === null && opts.config !== null && + !containsEntryInManifest( + entries, + relative(cwd, opts.config), + ) + ) { + wait("").start().warn( + `Config file ${opts.config} not found in the assets to be uploaded; any import map settings in the config file will not be applied during deployment. If this is not your intention, please check --include and --exclude options to make sure the config file is included.`, + ); + } + uploadSpinner = wait("Determining assets to upload...").start(); const neededHashes = await api.projectNegotiateAssets(project.id, { entries, diff --git a/src/utils/mod.ts b/src/utils/mod.ts index 65075ed1..ceaeb6fe 100644 --- a/src/utils/mod.ts +++ b/src/utils/mod.ts @@ -2,7 +2,7 @@ import { semverGreaterThanOrEquals, semverParse } from "../../deps.ts"; export { parseEntrypoint } from "./entrypoint.ts"; export { API, APIError } from "./api.ts"; -export { convertPatternToRegExp, walk } from "./walk.ts"; +export { convertPatternToRegExp, walk } from "./manifest.ts"; export { fromFileUrl, resolve } from "../../deps.ts"; // deno-lint-ignore no-explicit-any From 2f5a0a24b69bd05920e501d836f86fd18b8b5bb5 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 11 Aug 2024 17:41:47 +0900 Subject: [PATCH 05/11] add test case --- src/utils/manifest_test.ts | 47 +++++++++++++++++++ src/utils/manifest_testdata/complex/a.txt | 1 + .../manifest_testdata/complex/inner1/b.txt | 1 + .../manifest_testdata/complex/inner2/b.txt | 1 + 4 files changed, 50 insertions(+) create mode 100644 src/utils/manifest_testdata/complex/a.txt create mode 100644 src/utils/manifest_testdata/complex/inner1/b.txt create mode 100644 src/utils/manifest_testdata/complex/inner2/b.txt diff --git a/src/utils/manifest_test.ts b/src/utils/manifest_test.ts index 14c1c3d5..5afd8690 100644 --- a/src/utils/manifest_test.ts +++ b/src/utils/manifest_test.ts @@ -202,6 +202,53 @@ Deno.test({ ], }, }, + { + name: "complex", + input: { + testdir: "complex", + include: [], + exclude: [], + }, + expected: { + entries: { + "a.txt": { + kind: "file", + gitSha1: "78981922613b2afb6025042ff6bd878ac1994e85", + size: 2, + }, + "inner1": { + kind: "directory", + entries: { + "b.txt": { + kind: "file", + gitSha1: "61780798228d17af2d34fce4cfbdf35556832472", + size: 2, + }, + }, + }, + "inner2": { + kind: "directory", + entries: { + "b.txt": { + kind: "file", + gitSha1: "61780798228d17af2d34fce4cfbdf35556832472", + size: 2, + }, + }, + }, + }, + containedEntries: ["a.txt", "inner1/b.txt", "inner2/b.txt"], + notContainedEntries: [ + "b.txt", + "inner1/a.txt", + "inner2/a.txt", + ".git", + "deno.json", + "inner1", + "inner2", + ], + }, + }, ]; for (const test of tests) { diff --git a/src/utils/manifest_testdata/complex/a.txt b/src/utils/manifest_testdata/complex/a.txt new file mode 100644 index 00000000..78981922 --- /dev/null +++ b/src/utils/manifest_testdata/complex/a.txt @@ -0,0 +1 @@ +a diff --git a/src/utils/manifest_testdata/complex/inner1/b.txt b/src/utils/manifest_testdata/complex/inner1/b.txt new file mode 100644 index 00000000..61780798 --- /dev/null +++ b/src/utils/manifest_testdata/complex/inner1/b.txt @@ -0,0 +1 @@ +b diff --git a/src/utils/manifest_testdata/complex/inner2/b.txt b/src/utils/manifest_testdata/complex/inner2/b.txt new file mode 100644 index 00000000..61780798 --- /dev/null +++ b/src/utils/manifest_testdata/complex/inner2/b.txt @@ -0,0 +1 @@ +b From 5345829f200a2bbe4ad90737258ac371977df4d5 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 11 Aug 2024 18:00:54 +0900 Subject: [PATCH 06/11] yellow --- src/subcommands/deploy.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/subcommands/deploy.ts b/src/subcommands/deploy.ts index 623af02f..24cd32e1 100644 --- a/src/subcommands/deploy.ts +++ b/src/subcommands/deploy.ts @@ -1,6 +1,6 @@ // Copyright 2021 Deno Land Inc. All rights reserved. MIT license. -import { fromFileUrl, relative, type Spinner } from "../../deps.ts"; +import { fromFileUrl, relative, type Spinner, yellow } from "../../deps.ts"; import { envVarsFromArgs } from "../utils/env_vars.ts"; import { wait } from "../utils/spinner.ts"; import configFile from "../config_file.ts"; @@ -311,7 +311,9 @@ async function deploy(opts: DeployOpts): Promise { ) ) { wait("").start().warn( - `Config file ${opts.config} not found in the assets to be uploaded; any import map settings in the config file will not be applied during deployment. If this is not your intention, please check --include and --exclude options to make sure the config file is included.`, + yellow( + `Config file ${opts.config} not found in the assets to be uploaded; any import map settings in the config file will not be applied during deployment. If this is not your intention, please check --include and --exclude options to make sure the config file is included.`, + ), ); } From 6c31fbf6fefed56802297e77f71d110820f41331 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 11 Aug 2024 18:03:40 +0900 Subject: [PATCH 07/11] deno task build-action --- action/deps.js | 14 +++++++++++--- action/index.js | 13 ++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/action/deps.js b/action/deps.js index 6fd1219c..c4ac3421 100644 --- a/action/deps.js +++ b/action/deps.js @@ -4354,7 +4354,15 @@ function include(path, include, exclude) { } return true; } -async function walk(cwd, dir, files, options) { +async function walk(cwd, dir, options) { + const hashPathMap = new Map(); + const manifestEntries = await walkInner(cwd, dir, hashPathMap, options); + return { + manifestEntries, + hashPathMap + }; +} +async function walkInner(cwd, dir, hashPathMap, options) { const entries = {}; for await (const file of Deno.readDir(dir)){ const path = join2(dir, file.name); @@ -4371,12 +4379,12 @@ async function walk(cwd, dir, files, options) { gitSha1, size: data.byteLength }; - files.set(gitSha1, path); + hashPathMap.set(gitSha1, path); } else if (file.isDirectory) { if (relative === "/.git") continue; entry = { kind: "directory", - entries: await walk(cwd, path, files, options) + entries: await walkInner(cwd, path, hashPathMap, options) }; } else if (file.isSymlink) { const target = await Deno.readLink(path); diff --git a/action/index.js b/action/index.js index 252c50d5..77a613b6 100644 --- a/action/index.js +++ b/action/index.js @@ -79,11 +79,14 @@ async function main() { if (!includes.some((i) => i.includes("node_modules"))) { excludes.push("**/node_modules"); } - const assets = new Map(); - const entries = await walk(cwd, cwd, assets, { - include: includes.map(convertPatternToRegExp), - exclude: excludes.map(convertPatternToRegExp), - }); + const { manifestEntries: entries, hashPathMap: assets } = await walk( + cwd, + cwd, + { + include: includes.map(convertPatternToRegExp), + exclude: excludes.map(convertPatternToRegExp), + }, + ); core.debug(`Discovered ${assets.size} assets`); const api = new API(`GitHubOIDC ${token}`, ORIGIN); From 24ec201be67908d8a497c123dfbee772b1ac911c Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 11 Aug 2024 21:02:34 +0900 Subject: [PATCH 08/11] audocrlf false --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b910971e..f282de76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,12 @@ jobs: os: [macOS-latest, windows-latest, ubuntu-22.04-xl] steps: + # Some test cases are sensitive to line endings. Disable autocrlf on + # Windows to ensure consistent behavior. + - name: Disable autocrlf + if: runner.os == 'Windows' + run: git config --global core.autocrlf false + - name: Setup repo uses: actions/checkout@v3 From 1201f9c9d71427b7113cdeaacb3de19199d72134 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Tue, 29 Oct 2024 16:40:32 +0900 Subject: [PATCH 09/11] run deno task build-action --- action/deps.js | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/action/deps.js b/action/deps.js index 600f83fb..a27e2b4f 100644 --- a/action/deps.js +++ b/action/deps.js @@ -14,21 +14,6 @@ function isPathSeparator(code) { function isWindowsDeviceRoot(code) { return code >= 97 && code <= 122 || code >= 65 && code <= 90; } -function assertArg(url) { - url = url instanceof URL ? url : new URL(url); - if (url.protocol !== "file:") { - throw new TypeError("Must be a file URL."); - } - return url; -} -function fromFileUrl(url) { - url = assertArg(url); - let path = decodeURIComponent(url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25")).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\"); - if (url.hostname !== "") { - path = `\\\\${url.hostname}${path}`; - } - return path; -} function isAbsolute(path) { assertPath(path); const len = path.length; @@ -54,7 +39,7 @@ function assert(expr, msg = "") { throw new AssertionError(msg); } } -function assertArg1(path) { +function assertArg(path) { assertPath(path); if (path.length === 0) return "."; } @@ -112,7 +97,7 @@ function normalizeString(path, allowAboveRoot, separator, isPathSeparator) { return res; } function normalize(path) { - assertArg1(path); + assertArg(path); const len = path.length; let rootEnd = 0; let device; @@ -602,16 +587,12 @@ function isGlob(str) { function isPosixPathSeparator(code) { return code === 47; } -function fromFileUrl1(url) { - url = assertArg(url); - return decodeURIComponent(url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25")); -} function isAbsolute1(path) { assertPath(path); return path.length > 0 && isPosixPathSeparator(path.charCodeAt(0)); } function normalize1(path) { - assertArg1(path); + assertArg(path); const isAbsolute = isPosixPathSeparator(path.charCodeAt(0)); const trailingSeparator = isPosixPathSeparator(path.charCodeAt(path.length - 1)); path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator); @@ -694,9 +675,6 @@ const osType = (()=>{ return "linux"; })(); const isWindows = osType === "windows"; -function fromFileUrl2(url) { - return isWindows ? fromFileUrl(url) : fromFileUrl1(url); -} function join2(...paths) { return isWindows ? join(...paths) : join1(...paths); } @@ -3541,5 +3519,4 @@ function convertPatternToRegExp(pattern) { export { parseEntrypoint as parseEntrypoint }; export { API as API, APIError as APIError }; export { convertPatternToRegExp as convertPatternToRegExp, walk as walk }; -export { fromFileUrl2 as fromFileUrl, resolve2 as resolve }; From 440869a9beba44cac6bf763193f145a53ab67296 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Tue, 29 Oct 2024 16:58:10 +0900 Subject: [PATCH 10/11] export fromFileUrl and resolve --- src/utils/mod.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/utils/mod.ts b/src/utils/mod.ts index 95c8a616..738f4515 100644 --- a/src/utils/mod.ts +++ b/src/utils/mod.ts @@ -1,3 +1,5 @@ +// Export functions used by `action/index.js` export { parseEntrypoint } from "./entrypoint.ts"; export { API, APIError } from "./api.ts"; export { convertPatternToRegExp, walk } from "./manifest.ts"; +export { fromFileUrl, resolve } from "@std/path"; From 2371f4ba6ec6aad973984594a2c29713df6b83ea Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Tue, 29 Oct 2024 16:58:19 +0900 Subject: [PATCH 11/11] run deno task build-action --- action/deps.js | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/action/deps.js b/action/deps.js index a27e2b4f..600f83fb 100644 --- a/action/deps.js +++ b/action/deps.js @@ -14,6 +14,21 @@ function isPathSeparator(code) { function isWindowsDeviceRoot(code) { return code >= 97 && code <= 122 || code >= 65 && code <= 90; } +function assertArg(url) { + url = url instanceof URL ? url : new URL(url); + if (url.protocol !== "file:") { + throw new TypeError("Must be a file URL."); + } + return url; +} +function fromFileUrl(url) { + url = assertArg(url); + let path = decodeURIComponent(url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25")).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\"); + if (url.hostname !== "") { + path = `\\\\${url.hostname}${path}`; + } + return path; +} function isAbsolute(path) { assertPath(path); const len = path.length; @@ -39,7 +54,7 @@ function assert(expr, msg = "") { throw new AssertionError(msg); } } -function assertArg(path) { +function assertArg1(path) { assertPath(path); if (path.length === 0) return "."; } @@ -97,7 +112,7 @@ function normalizeString(path, allowAboveRoot, separator, isPathSeparator) { return res; } function normalize(path) { - assertArg(path); + assertArg1(path); const len = path.length; let rootEnd = 0; let device; @@ -587,12 +602,16 @@ function isGlob(str) { function isPosixPathSeparator(code) { return code === 47; } +function fromFileUrl1(url) { + url = assertArg(url); + return decodeURIComponent(url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25")); +} function isAbsolute1(path) { assertPath(path); return path.length > 0 && isPosixPathSeparator(path.charCodeAt(0)); } function normalize1(path) { - assertArg(path); + assertArg1(path); const isAbsolute = isPosixPathSeparator(path.charCodeAt(0)); const trailingSeparator = isPosixPathSeparator(path.charCodeAt(path.length - 1)); path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator); @@ -675,6 +694,9 @@ const osType = (()=>{ return "linux"; })(); const isWindows = osType === "windows"; +function fromFileUrl2(url) { + return isWindows ? fromFileUrl(url) : fromFileUrl1(url); +} function join2(...paths) { return isWindows ? join(...paths) : join1(...paths); } @@ -3519,4 +3541,5 @@ function convertPatternToRegExp(pattern) { export { parseEntrypoint as parseEntrypoint }; export { API as API, APIError as APIError }; export { convertPatternToRegExp as convertPatternToRegExp, walk as walk }; +export { fromFileUrl2 as fromFileUrl, resolve2 as resolve };