workspace-tools 0.23.1 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.json CHANGED
@@ -2,7 +2,52 @@
2
2
  "name": "workspace-tools",
3
3
  "entries": [
4
4
  {
5
- "date": "Fri, 15 Jul 2022 03:59:36 GMT",
5
+ "date": "Wed, 20 Jul 2022 22:31:28 GMT",
6
+ "tag": "workspace-tools_v0.24.0",
7
+ "version": "0.24.0",
8
+ "comments": {
9
+ "minor": [
10
+ {
11
+ "author": "elcraig@microsoft.com",
12
+ "package": "workspace-tools",
13
+ "comment": "Make getDefaultRemote properly handle more combinations of URL formats, and add more logging to encourage defining the `repository` property in package.json for more accurate detection",
14
+ "commit": "4bffdc45ceb49ca49f28ebc788e5c2cbe71e3a9f"
15
+ }
16
+ ]
17
+ }
18
+ },
19
+ {
20
+ "date": "Fri, 15 Jul 2022 07:09:50 GMT",
21
+ "tag": "workspace-tools_v0.23.3",
22
+ "version": "0.23.3",
23
+ "comments": {
24
+ "patch": [
25
+ {
26
+ "author": "ken@gizzar.com",
27
+ "package": "workspace-tools",
28
+ "comment": "change the api to be restored to have getDependentMap (actually gets dependencies)",
29
+ "commit": "4228af963584e87f6ba028f6e66abf7c6ae30700"
30
+ }
31
+ ]
32
+ }
33
+ },
34
+ {
35
+ "date": "Fri, 15 Jul 2022 05:05:48 GMT",
36
+ "tag": "workspace-tools_v0.23.2",
37
+ "version": "0.23.2",
38
+ "comments": {
39
+ "patch": [
40
+ {
41
+ "author": "ken@gizzar.com",
42
+ "package": "workspace-tools",
43
+ "comment": "fixing the dependent map results",
44
+ "commit": "6dff4d2ba6fe88ab0a0f60c7c62410393e4596ee"
45
+ }
46
+ ]
47
+ }
48
+ },
49
+ {
50
+ "date": "Fri, 15 Jul 2022 03:59:39 GMT",
6
51
  "tag": "workspace-tools_v0.23.1",
7
52
  "version": "0.23.1",
8
53
  "comments": {
package/CHANGELOG.md CHANGED
@@ -1,12 +1,36 @@
1
1
  # Change Log - workspace-tools
2
2
 
3
- This log was last generated on Fri, 15 Jul 2022 03:59:36 GMT and should not be manually modified.
3
+ This log was last generated on Wed, 20 Jul 2022 22:31:28 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 0.24.0
8
+
9
+ Wed, 20 Jul 2022 22:31:28 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - Make getDefaultRemote properly handle more combinations of URL formats, and add more logging to encourage defining the `repository` property in package.json for more accurate detection (elcraig@microsoft.com)
14
+
15
+ ## 0.23.3
16
+
17
+ Fri, 15 Jul 2022 07:09:50 GMT
18
+
19
+ ### Patches
20
+
21
+ - change the api to be restored to have getDependentMap (actually gets dependencies) (ken@gizzar.com)
22
+
23
+ ## 0.23.2
24
+
25
+ Fri, 15 Jul 2022 05:05:48 GMT
26
+
27
+ ### Patches
28
+
29
+ - fixing the dependent map results (ken@gizzar.com)
30
+
7
31
  ## 0.23.1
8
32
 
9
- Fri, 15 Jul 2022 03:59:36 GMT
33
+ Fri, 15 Jul 2022 03:59:39 GMT
10
34
 
11
35
  ### Patches
12
36
 
@@ -0,0 +1,28 @@
1
+ export declare type GetDefaultRemoteOptions = {
2
+ /** Get repository info relative to this directory. */
3
+ cwd: string;
4
+ /**
5
+ * If true, throw an error if remote info can't be found, or if a `repository` is not specified
6
+ * in package.json and no matching remote is found.
7
+ */
8
+ strict?: boolean;
9
+ /** If true, log debug messages about how the remote was chosen */
10
+ verbose?: boolean;
11
+ };
12
+ /**
13
+ * Get the name of the default remote: the one matching the `repository` field in package.json.
14
+ * Throws if `options.cwd` is not in a git repo or there's no package.json at the repo root.
15
+ *
16
+ * The order of preference for returned remotes is:
17
+ * 1. If `repository` is defined in package.json, the remote with a matching URL (if `options.strict`
18
+ * is true, throws an error if no matching remote exists)
19
+ * 2. `upstream` if defined
20
+ * 3. `origin` if defined
21
+ * 4. The first defined remote
22
+ * 5. If there are no defined remotes: throws an error if `options.strict` is true; otherwise returns `origin`
23
+ *
24
+ * @returns The name of the inferred default remote.
25
+ */
26
+ export declare function getDefaultRemote(options: GetDefaultRemoteOptions): string;
27
+ /** @deprecated Use the object param version */
28
+ export declare function getDefaultRemote(cwd: string): string;
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getDefaultRemote = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const paths_1 = require("../paths");
10
+ const getRepositoryName_1 = require("./getRepositoryName");
11
+ const git_1 = require("./git");
12
+ function getDefaultRemote(cwdOrOptions) {
13
+ const options = typeof cwdOrOptions === "string" ? { cwd: cwdOrOptions } : cwdOrOptions;
14
+ const { cwd, strict, verbose } = options;
15
+ const log = (message) => verbose && console.log(message);
16
+ const logOrThrow = (message) => {
17
+ if (strict) {
18
+ throw new Error(message);
19
+ }
20
+ log(message);
21
+ };
22
+ const gitRoot = (0, paths_1.findGitRoot)(cwd);
23
+ if (!gitRoot) {
24
+ throw new Error(`Directory "${cwd}" does not appear to be in a git repository`);
25
+ }
26
+ let packageJson = {};
27
+ const packageJsonPath = path_1.default.join(gitRoot, "package.json");
28
+ try {
29
+ packageJson = fs_extra_1.default.readJSONSync(packageJsonPath);
30
+ }
31
+ catch (e) {
32
+ throw new Error(`Could not read "${packageJsonPath}"`);
33
+ }
34
+ const { repository } = packageJson;
35
+ const repositoryUrl = typeof repository === "string" ? repository : (repository && repository.url) || "";
36
+ if (!repositoryUrl) {
37
+ // This is always logged because it's strongly recommended to fix
38
+ console.log(`Valid "repository" key not found in "${packageJsonPath}". Consider adding this info for more accurate git remote detection.`);
39
+ }
40
+ /** Repository full name (owner and repo name) specified in package.json */
41
+ const repositoryName = (0, getRepositoryName_1.getRepositoryName)(repositoryUrl);
42
+ const remotesResult = (0, git_1.git)(["remote", "-v"], { cwd });
43
+ if (!remotesResult.success) {
44
+ logOrThrow(`Could not determine available git remotes under "${cwd}"`);
45
+ }
46
+ /** Mapping from remote URL to full name (owner and repo name) */
47
+ const remotes = {};
48
+ remotesResult.stdout.split("\n").forEach((line) => {
49
+ const [remoteName, remoteUrl] = line.split(/\s+/);
50
+ const remoteRepoName = (0, getRepositoryName_1.getRepositoryName)(remoteUrl);
51
+ if (remoteRepoName) {
52
+ remotes[remoteRepoName] = remoteName;
53
+ }
54
+ });
55
+ if (repositoryName) {
56
+ // If the repository name was found in package.json, check for a matching remote
57
+ if (remotes[repositoryName]) {
58
+ return remotes[repositoryName];
59
+ }
60
+ // If `strict` is true, and repositoryName is found, there MUST be a matching remote
61
+ logOrThrow(`Could not find remote pointing to repository "${repositoryName}".`);
62
+ }
63
+ // Default to upstream or origin if available, or the first remote otherwise
64
+ const allRemoteNames = Object.values(remotes);
65
+ const fallbacks = ["upstream", "origin", ...allRemoteNames];
66
+ for (const fallback of fallbacks) {
67
+ if (allRemoteNames.includes(fallback)) {
68
+ log(`Default to remote "${fallback}"`);
69
+ return fallback;
70
+ }
71
+ }
72
+ // If we get here, no git remotes were found. This should probably always be an error (since
73
+ // subsequent operations which require a remote likely won't work), but to match old behavior,
74
+ // still default to "origin" unless `strict` is true.
75
+ logOrThrow(`Could not find any remotes in git repo at "${gitRoot}".`);
76
+ log(`Assuming default remote "origin".`);
77
+ return "origin";
78
+ }
79
+ exports.getDefaultRemote = getDefaultRemote;
@@ -0,0 +1,18 @@
1
+ import { GetDefaultRemoteOptions } from "./getDefaultRemote";
2
+ export declare type GetDefaultRemoteBranchOptions = GetDefaultRemoteOptions & {
3
+ /** Name of branch to use. If undefined, uses the default branch name (falling back to `master`). */
4
+ branch?: string;
5
+ };
6
+ /**
7
+ * Gets a reference to `options.branch` or the default branch relative to the default remote.
8
+ * (See {@link getDefaultRemote} for how the default remote is determined.)
9
+ * Throws if `options.cwd` is not in a git repo or there's no package.json at the repo root.
10
+ * @returns A branch reference like `upstream/master` or `origin/master`.
11
+ */
12
+ export declare function getDefaultRemoteBranch(options: GetDefaultRemoteBranchOptions): string;
13
+ /**
14
+ * First param: `branch`. Second param: `cwd`. See {@link GetDefaultRemoteBranchOptions} for more info.
15
+ * (This had to be changed to `...args` to avoid a conflict with the object param version.)
16
+ * @deprecated Use the object param version
17
+ */
18
+ export declare function getDefaultRemoteBranch(...args: string[]): string;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDefaultRemoteBranch = void 0;
4
+ const getDefaultRemote_1 = require("./getDefaultRemote");
5
+ const git_1 = require("./git");
6
+ const gitUtilities_1 = require("./gitUtilities");
7
+ function getDefaultRemoteBranch(...args) {
8
+ var _a;
9
+ const [branchOrOptions, argsCwd] = args;
10
+ const options = typeof branchOrOptions === "string"
11
+ ? { branch: branchOrOptions, cwd: argsCwd }
12
+ : branchOrOptions;
13
+ const { cwd, branch } = options;
14
+ const defaultRemote = (0, getDefaultRemote_1.getDefaultRemote)(options);
15
+ if (branch) {
16
+ return `${defaultRemote}/${branch}`;
17
+ }
18
+ const showRemote = (0, git_1.git)(["remote", "show", defaultRemote], { cwd });
19
+ let remoteDefaultBranch;
20
+ if (showRemote.success) {
21
+ /**
22
+ * `showRemote.stdout` is something like this:
23
+ *
24
+ * * remote origin
25
+ * Fetch URL: .../monorepo-upstream
26
+ * Push URL: .../monorepo-upstream
27
+ * HEAD branch: main
28
+ */
29
+ remoteDefaultBranch = (_a = showRemote.stdout
30
+ .split(/\n/)
31
+ .find((line) => line.includes("HEAD branch"))) === null || _a === void 0 ? void 0 : _a.replace(/^\s*HEAD branch:\s+/, "");
32
+ }
33
+ return `${defaultRemote}/${remoteDefaultBranch || (0, gitUtilities_1.getDefaultBranch)(cwd)}`;
34
+ }
35
+ exports.getDefaultRemoteBranch = getDefaultRemoteBranch;
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Get a repository full name (owner and repo, plus organization for ADO/VSO) from a repository URL,
3
+ * including special handling for the many ADO/VSO URL formats.
4
+ *
5
+ * Examples:
6
+ * - returns `microsoft/workspace-tools` for `https://github.com/microsoft/workspace-tools.git`
7
+ * - returns `foo/bar/some-repo` for `https://dev.azure.com/foo/bar/_git/some-repo`
8
+ */
9
+ export declare function getRepositoryName(url: string): string;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getRepositoryName = void 0;
7
+ const git_url_parse_1 = __importDefault(require("git-url-parse"));
8
+ /**
9
+ * Get a repository full name (owner and repo, plus organization for ADO/VSO) from a repository URL,
10
+ * including special handling for the many ADO/VSO URL formats.
11
+ *
12
+ * Examples:
13
+ * - returns `microsoft/workspace-tools` for `https://github.com/microsoft/workspace-tools.git`
14
+ * - returns `foo/bar/some-repo` for `https://dev.azure.com/foo/bar/_git/some-repo`
15
+ */
16
+ function getRepositoryName(url) {
17
+ var _a;
18
+ try {
19
+ // Mostly use this standard library, but fix some VSO/ADO-specific quirks to account for the
20
+ // fact that all of the following URLs should be considered to point to the same repo:
21
+ // https://foo.visualstudio.com/bar/_git/some-repo
22
+ // https://foo.visualstudio.com/DefaultCollection/bar/_git/some-repo
23
+ // https://user:token@foo.visualstudio.com/DefaultCollection/bar/_git/some-repo
24
+ // https://foo.visualstudio.com/DefaultCollection/bar/_git/_optimized/some-repo
25
+ // foo@vs-ssh.visualstudio.com:v3/foo/bar/some-repo
26
+ // https://dev.azure.com/foo/bar/_git/some-repo
27
+ // https://dev.azure.com/foo/bar/_git/_optimized/some-repo
28
+ // https://user@dev.azure.com/foo/bar/_git/some-repo
29
+ // git@ssh.dev.azure.com:v3/foo/bar/some-repo
30
+ let fixedUrl = url.replace("/_optimized/", "/").replace("/DefaultCollection/", "/");
31
+ const parsedUrl = (0, git_url_parse_1.default)(fixedUrl);
32
+ const isVSO = fixedUrl.includes(".visualstudio.com");
33
+ const isADO = fixedUrl.includes("dev.azure.com");
34
+ if (!isVSO && !isADO) {
35
+ return parsedUrl.full_name;
36
+ }
37
+ // As of writing, ADO and VSO SSH URLs are parsed completely wrong
38
+ const sshMatch = parsedUrl.full_name.match(/(vs-ssh\.visualstudio\.com|ssh\.dev\.azure\.com):v\d+\/([^/]+)\/([^/]+)/);
39
+ if (sshMatch) {
40
+ return `${sshMatch[2]}/${sshMatch[3]}/${parsedUrl.name}`;
41
+ }
42
+ // As of writing, full_name is wrong for enough variants of ADO and VSO URLs that it
43
+ // makes more sense to just build it manually.
44
+ let organization = parsedUrl.organization;
45
+ if (!organization && isVSO) {
46
+ // organization is missing or wrong for VSO
47
+ organization = (_a = parsedUrl.resource.match(/([^.@]+)\.visualstudio\.com/)) === null || _a === void 0 ? void 0 : _a[1];
48
+ }
49
+ return `${organization}/${parsedUrl.owner}/${parsedUrl.name}`;
50
+ }
51
+ catch (err) {
52
+ return "";
53
+ }
54
+ }
55
+ exports.getRepositoryName = getRepositoryName;
@@ -0,0 +1,29 @@
1
+ /// <reference types="node" />
2
+ import { SpawnSyncOptions } from "child_process";
3
+ export declare class GitError extends Error {
4
+ originalError: unknown;
5
+ constructor(message: string, originalError?: unknown);
6
+ }
7
+ export declare type GitProcessOutput = {
8
+ stderr: string;
9
+ stdout: string;
10
+ success: boolean;
11
+ };
12
+ /** Observes the git operations called from `git()` or `gitFailFast()` */
13
+ declare type GitObserver = (args: string[], output: GitProcessOutput) => void;
14
+ /**
15
+ * Adds an observer for the git operations, e.g. for testing
16
+ * @param observer
17
+ */
18
+ export declare function addGitObserver(observer: GitObserver): void;
19
+ /**
20
+ * Runs git command - use this for read-only commands
21
+ */
22
+ export declare function git(args: string[], options?: SpawnSyncOptions): GitProcessOutput;
23
+ /**
24
+ * Runs git command - use this for commands that make changes to the filesystem
25
+ */
26
+ export declare function gitFailFast(args: string[], options?: SpawnSyncOptions & {
27
+ noExitCode?: boolean;
28
+ }): void;
29
+ export {};
package/lib/git/git.js ADDED
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ //
3
+ // Basic git wrappers
4
+ //
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.gitFailFast = exports.git = exports.addGitObserver = exports.GitError = void 0;
7
+ const child_process_1 = require("child_process");
8
+ class GitError extends Error {
9
+ constructor(message, originalError) {
10
+ if (originalError instanceof Error) {
11
+ super(`${message}: ${originalError.message}`);
12
+ }
13
+ else {
14
+ super(message);
15
+ }
16
+ this.originalError = originalError;
17
+ }
18
+ }
19
+ exports.GitError = GitError;
20
+ /**
21
+ * A global maxBuffer override for all git operations.
22
+ * Bumps up the default to 500MB instead of 1MB.
23
+ * Override this value with the `GIT_MAX_BUFFER` environment variable.
24
+ */
25
+ const defaultMaxBuffer = process.env.GIT_MAX_BUFFER ? parseInt(process.env.GIT_MAX_BUFFER) : 500 * 1024 * 1024;
26
+ const observers = [];
27
+ let observing;
28
+ /**
29
+ * Adds an observer for the git operations, e.g. for testing
30
+ * @param observer
31
+ */
32
+ function addGitObserver(observer) {
33
+ observers.push(observer);
34
+ }
35
+ exports.addGitObserver = addGitObserver;
36
+ /**
37
+ * Runs git command - use this for read-only commands
38
+ */
39
+ function git(args, options) {
40
+ const results = (0, child_process_1.spawnSync)("git", args, Object.assign({ maxBuffer: defaultMaxBuffer }, options));
41
+ const output = {
42
+ stderr: results.stderr.toString().trimRight(),
43
+ stdout: results.stdout.toString().trimRight(),
44
+ success: results.status === 0,
45
+ };
46
+ // notify observers, flipping the observing bit to prevent infinite loops
47
+ if (!observing) {
48
+ observing = true;
49
+ for (const observer of observers) {
50
+ observer(args, output);
51
+ }
52
+ observing = false;
53
+ }
54
+ return output;
55
+ }
56
+ exports.git = git;
57
+ /**
58
+ * Runs git command - use this for commands that make changes to the filesystem
59
+ */
60
+ function gitFailFast(args, options) {
61
+ var _a, _b;
62
+ const gitResult = git(args, options);
63
+ if (!gitResult.success) {
64
+ if (!(options === null || options === void 0 ? void 0 : options.noExitCode)) {
65
+ process.exitCode = 1;
66
+ }
67
+ throw new GitError(`CRITICAL ERROR: running git command: git ${args.join(" ")}!
68
+ ${(_a = gitResult.stdout) === null || _a === void 0 ? void 0 : _a.toString().trimRight()}
69
+ ${(_b = gitResult.stderr) === null || _b === void 0 ? void 0 : _b.toString().trimRight()}`);
70
+ }
71
+ }
72
+ exports.gitFailFast = gitFailFast;
@@ -1,25 +1,3 @@
1
- /// <reference types="node" />
2
- import { SpawnSyncOptions } from "child_process";
3
- declare type ProcessOutput = {
4
- stderr: string;
5
- stdout: string;
6
- success: boolean;
7
- };
8
- /** Observes the git operations called from `git()` or `gitFailFast()` */
9
- declare type GitObserver = (args: string[], output: ProcessOutput) => void;
10
- /**
11
- * Adds an observer for the git operations, e.g. for testing
12
- * @param observer
13
- */
14
- export declare function addGitObserver(observer: GitObserver): void;
15
- /**
16
- * Runs git command - use this for read-only commands
17
- */
18
- export declare function git(args: string[], options?: SpawnSyncOptions): ProcessOutput;
19
- /**
20
- * Runs git command - use this for commands that make changes to the filesystem
21
- */
22
- export declare function gitFailFast(args: string[], options?: SpawnSyncOptions): void;
23
1
  export declare function getUntrackedChanges(cwd: string): string[];
24
2
  export declare function fetchRemote(remote: string, cwd: string): void;
25
3
  export declare function fetchRemoteBranch(remote: string, remoteBranch: string, cwd: string): void;
@@ -31,8 +9,6 @@ export declare function getUnstagedChanges(cwd: string): string[];
31
9
  export declare function getChanges(branch: string, cwd: string): string[];
32
10
  /**
33
11
  * Gets all the changes between the branch and the merge-base
34
- * @param branch
35
- * @param cwd
36
12
  */
37
13
  export declare function getBranchChanges(branch: string, cwd: string): string[];
38
14
  export declare function getChangesBetweenRefs(fromRef: string, toRef: string, options: string[], pattern: string, cwd: string): string[];
@@ -58,8 +34,8 @@ export declare function parseRemoteBranch(branch: string): {
58
34
  remote: string;
59
35
  remoteBranch: string;
60
36
  };
61
- export declare function getDefaultRemoteBranch(branch: string | undefined, cwd: string): string;
37
+ /**
38
+ * Gets the default branch based on `git config init.defaultBranch`, falling back to `master`.
39
+ */
62
40
  export declare function getDefaultBranch(cwd: string): string;
63
- export declare function getDefaultRemote(cwd: string): string;
64
41
  export declare function listAllTrackedFiles(patterns: string[], cwd: string): string[];
65
- export {};