Skip to content

Commit 0df34ed

Browse files
committed
fix: fetch GitHub LFS over HTTPS
1 parent d5440a2 commit 0df34ed

3 files changed

Lines changed: 139 additions & 2 deletions

File tree

azure-pipelines.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ stages:
141141
- script: |
142142
set -euo pipefail
143143
echo "Cloning $(repoUrl) @ $(repoBranch)"
144-
GIT_CLONE_PROTECTION_ACTIVE=false git \
144+
GIT_CLONE_PROTECTION_ACTIVE=false GIT_LFS_SKIP_SMUDGE=1 git \
145145
-c init.templateDir=/dev/null \
146146
-c core.hooksPath=/dev/null \
147147
clone --recursive --shallow-submodules --depth 1 --branch "$(repoBranch)" "$(repoUrl)" "$(repoDir)"
@@ -152,11 +152,22 @@ stages:
152152
HOME: $(gitHomeDir)
153153
XDG_CONFIG_HOME: $(gitConfigDir)
154154
155+
- script: |
156+
set -euo pipefail
157+
node configureGitLfsUrl.js "$(repoDir)"
158+
git submodule foreach --recursive 'node "$(Build.SourcesDirectory)/configureGitLfsUrl.js" "."'
159+
displayName: "Configure Git LFS transport"
160+
condition: eq(variables['packageSourceMode'], 'git')
161+
env:
162+
HOME: $(gitHomeDir)
163+
XDG_CONFIG_HOME: $(gitConfigDir)
164+
155165
- script: |
156166
set -euo pipefail
157167
cd "$(repoDir)"
158168
echo "Fetching Git LFS objects for $(repoDir)"
159169
git lfs pull
170+
git lfs checkout
160171
displayName: "Fetch Git LFS"
161172
condition: eq(variables['packageSourceMode'], 'git')
162173
env:
@@ -167,7 +178,7 @@ stages:
167178
set -euo pipefail
168179
cd "$(repoDir)"
169180
echo "Fetching Git LFS objects for submodules in $(repoDir)"
170-
git submodule foreach --recursive 'git lfs pull'
181+
git submodule foreach --recursive 'git lfs pull && git lfs checkout'
171182
displayName: "Fetch submodule Git LFS"
172183
condition: eq(variables['packageSourceMode'], 'git')
173184
env:

configureGitLfsUrl.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
"use strict";
2+
3+
const { execFileSync } = require("child_process");
4+
5+
/**
6+
* @param {string} repoUrl
7+
* @returns {string | null}
8+
*/
9+
function getGitHubLfsUrl(repoUrl) {
10+
let url;
11+
try {
12+
url = new URL(repoUrl);
13+
} catch {
14+
const scpMatch = repoUrl.match(
15+
/^git@github\.com:([^/]+)\/(.+?)(?:\.git)?$/,
16+
);
17+
if (!scpMatch) return null;
18+
return `https://github.com/${scpMatch[1]}/${scpMatch[2]}.git/info/lfs`;
19+
}
20+
21+
if (url.protocol !== "https:" && url.protocol !== "ssh:") return null;
22+
if (url.hostname.toLowerCase() !== "github.com") return null;
23+
if (url.protocol === "ssh:" && url.username && url.username !== "git") {
24+
return null;
25+
}
26+
27+
const parts = url.pathname.replace(/^\/+|\/+$/g, "").split("/");
28+
if (parts.length !== 2 || !parts[0] || !parts[1]) return null;
29+
30+
const repoName = parts[1].replace(/\.git$/i, "");
31+
return `https://github.com/${parts[0]}/${repoName}.git/info/lfs`;
32+
}
33+
34+
/**
35+
* @param {string} repoDir
36+
* @returns {string}
37+
*/
38+
function getOriginUrl(repoDir) {
39+
return execFileSync(
40+
"git",
41+
["-C", repoDir, "config", "--get", "remote.origin.url"],
42+
{
43+
encoding: "utf8",
44+
},
45+
).trim();
46+
}
47+
48+
/**
49+
* @param {string} repoDir
50+
* @param {string} lfsUrl
51+
*/
52+
function setGitLfsUrl(repoDir, lfsUrl) {
53+
execFileSync("git", ["-C", repoDir, "config", "--local", "lfs.url", lfsUrl], {
54+
stdio: "inherit",
55+
});
56+
}
57+
58+
/**
59+
* @param {string} repoDir
60+
* @returns {string | null}
61+
*/
62+
function configureGitLfsUrl(repoDir) {
63+
const originUrl = getOriginUrl(repoDir);
64+
const lfsUrl = getGitHubLfsUrl(originUrl);
65+
if (!lfsUrl) return null;
66+
67+
setGitLfsUrl(repoDir, lfsUrl);
68+
return lfsUrl;
69+
}
70+
71+
function main() {
72+
const repoDir = process.argv[2] || ".";
73+
const lfsUrl = configureGitLfsUrl(repoDir);
74+
if (lfsUrl) {
75+
console.log(`Configured Git LFS URL for GitHub remote: ${lfsUrl}`);
76+
} else {
77+
console.log(
78+
"Repository remote is not a github.com URL; leaving Git LFS URL unchanged.",
79+
);
80+
}
81+
}
82+
83+
module.exports = {
84+
configureGitLfsUrl,
85+
getGitHubLfsUrl,
86+
};
87+
88+
if (require.main === module) {
89+
main();
90+
}

test/test-configureGitLfsUrl.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
"use strict";
2+
/* global describe, it */
3+
4+
const should = require("should");
5+
6+
const { getGitHubLfsUrl } = require("../configureGitLfsUrl");
7+
8+
describe("configureGitLfsUrl.js", function () {
9+
it("normalizes HTTPS GitHub remotes to the Git LFS endpoint", function () {
10+
getGitHubLfsUrl("https://github.com/bluecadet/unity-packages").should.equal(
11+
"https://github.com/bluecadet/unity-packages.git/info/lfs",
12+
);
13+
getGitHubLfsUrl(
14+
"https://github.com/bluecadet/unity-packages.git",
15+
).should.equal("https://github.com/bluecadet/unity-packages.git/info/lfs");
16+
});
17+
18+
it("normalizes GitHub SSH remotes to the public HTTPS Git LFS endpoint", function () {
19+
getGitHubLfsUrl(
20+
"ssh://git@github.com/bluecadet/unity-packages.git",
21+
).should.equal("https://github.com/bluecadet/unity-packages.git/info/lfs");
22+
getGitHubLfsUrl("git@github.com:bluecadet/unity-packages.git").should.equal(
23+
"https://github.com/bluecadet/unity-packages.git/info/lfs",
24+
);
25+
});
26+
27+
it("does not rewrite non-GitHub remotes", function () {
28+
should(
29+
getGitHubLfsUrl("https://gitlab.com/bluecadet/unity-packages"),
30+
).equal(null);
31+
should(
32+
getGitHubLfsUrl("https://github.example.com/bluecadet/unity-packages"),
33+
).equal(null);
34+
should(getGitHubLfsUrl("file:///tmp/unity-packages")).equal(null);
35+
});
36+
});

0 commit comments

Comments
 (0)