1 | import * as wmillclient from "windmill-client"; |
2 | import wmill from "windmill-cli"; |
3 | import { basename } from "node:path"; |
4 | const util = require("util"); |
5 | const exec = util.promisify(require("child_process").exec); |
6 | import process from "process"; |
7 |
|
8 | export async function main( |
9 | repository: any, |
10 | ref: string, |
11 | user_username: string, |
12 | commits: any |
13 | ) { |
14 | const ws = process.env["WM_WORKSPACE"]; |
15 | const repo_name = repository.name; |
16 | const message: string = commits.pop().message; |
17 |
|
18 | console.log("Received git sync push request:"); |
19 | console.log(`Repo: ${repo_name}`); |
20 | console.log(`Ref: ${ref}`); |
21 | console.log(`Pusher: ${user_username}`); |
22 | console.log(`Message: ${message}`); |
23 |
|
24 | const ws_settings = await wmillclient.WorkspaceService.getSettings({ |
25 | workspace: ws, |
26 | }); |
27 | const repos = ws_settings.git_sync?.repositories; |
28 |
|
29 | if (message.startsWith("[WM]")) { |
30 | return { |
31 | success: true, |
32 | message: "Commit coming from windmill, no need to cli push back!", |
33 | }; |
34 | } |
35 |
|
36 | if (repos && repos.length > 0) { |
37 | for (const repo of repos) { |
38 | const repo_resource = await wmillclient.getResource( |
39 | repo.git_repo_resource_path.split("$res:").pop() |
40 | ); |
41 | if (repo_resource.url?.toLowerCase().includes(repo_name.toLowerCase())) { |
42 | console.log("Repo found..."); |
43 | const cwd = process.cwd(); |
44 | process.env["HOME"] = "."; |
45 |
|
46 | await git_clone(cwd, repo_resource, true); |
47 |
|
48 | await wmill_sync_push(ws, message); |
49 | console.log("Finished syncing"); |
50 | process.chdir(`${cwd}`); |
51 | return { success: true }; |
52 | } |
53 | } |
54 | } |
55 | } |
56 |
|
57 | async function git_clone( |
58 | cwd: string, |
59 | repo_resource: any, |
60 | use_individual_branch: boolean |
61 | ): Promise<string> { |
62 | let repo_url = repo_resource.url; |
63 | const subfolder = repo_resource.folder ?? ""; |
64 | const branch = repo_resource.branch ?? ""; |
65 | const repo_name = basename(repo_url, ".git"); |
66 | const azureMatch = repo_url.match(/AZURE_DEVOPS_TOKEN\((?<url>.+)\)/); |
67 | if (azureMatch) { |
68 | console.log( |
69 | "Requires Azure DevOps service account access token, requesting..." |
70 | ); |
71 | const azureResource = await wmillclient.getResource(azureMatch.groups.url); |
72 | const response = await fetch( |
73 | `https://login.microsoftonline.com/${azureResource.azureTenantId}/oauth2/token`, |
74 | { |
75 | method: "POST", |
76 | body: new URLSearchParams({ |
77 | client_id: azureResource.azureClientId, |
78 | client_secret: azureResource.azureClientSecret, |
79 | grant_type: "client_credentials", |
80 | resource: "499b84ac-1321-427f-aa17-267ca6975798/.default", |
81 | }), |
82 | } |
83 | ); |
84 | const { access_token } = await response.json(); |
85 | repo_url = repo_url.replace(azureMatch[0], access_token); |
86 | } |
87 | const args = ["clone", "--quiet", "--depth", "1"]; |
88 | if (use_individual_branch) { |
89 | args.push("--no-single-branch"); |
90 | } |
91 | if (subfolder !== "") { |
92 | args.push("--sparse"); |
93 | } |
94 | if (branch !== "") { |
95 | args.push("--branch"); |
96 | args.push(branch); |
97 | } |
98 | args.push(repo_url); |
99 | args.push(repo_name); |
100 | await sh_run(-1, "git", ...args); |
101 | try { |
102 | process.chdir(`${cwd}/${repo_name}`); |
103 | } catch (err) { |
104 | console.log( |
105 | `Error changing directory to '${cwd}/${repo_name}'. Error was:\n${err}` |
106 | ); |
107 | throw err; |
108 | } |
109 | process.chdir(`${cwd}/${repo_name}`); |
110 | if (subfolder !== "") { |
111 | await sh_run(undefined, "git", "sparse-checkout", "add", subfolder); |
112 | } |
113 | try { |
114 | process.chdir(`${cwd}/${repo_name}/${subfolder}`); |
115 | } catch (err) { |
116 | console.log( |
117 | `Error changing directory to '${cwd}/${repo_name}/${subfolder}'. Error was:\n${err}` |
118 | ); |
119 | throw err; |
120 | } |
121 | return repo_name; |
122 | } |
123 |
|
124 | async function sh_run( |
125 | secret_position: number | undefined, |
126 | cmd: string, |
127 | ...args: string[] |
128 | ) { |
129 | const nargs = secret_position != undefined ? args.slice() : args; |
130 | if (secret_position && secret_position < 0) { |
131 | secret_position = nargs.length - 1 + secret_position; |
132 | } |
133 | let secret: string | undefined = undefined; |
134 | if (secret_position != undefined) { |
135 | nargs[secret_position] = "***"; |
136 | secret = args[secret_position]; |
137 | } |
138 |
|
139 | console.log(`Running '${cmd} ${nargs.join(" ")} ...'`); |
140 | const command = exec(`${cmd} ${args.join(" ")}`); |
141 | try { |
142 | const { stdout, stderr } = await command; |
143 | if (stdout.length > 0) { |
144 | console.log(stdout); |
145 | } |
146 | if (stderr.length > 0) { |
147 | console.log(stderr); |
148 | } |
149 | console.log("Command successfully executed"); |
150 | return stdout; |
151 | } catch (error) { |
152 | let errorString = error.toString(); |
153 | if (secret) { |
154 | errorString = errorString.replace(secret, "***"); |
155 | } |
156 | const err = `SH command '${cmd} ${nargs.join( |
157 | " " |
158 | )}' returned with error ${errorString}`; |
159 | throw Error(err); |
160 | } |
161 | } |
162 |
|
163 | async function wmill_sync_push(workspace_id: string, message: string) { |
164 | console.log("Pushing workspace to windmill"); |
165 | await wmill_run( |
166 | 3, |
167 | "sync", |
168 | "push", |
169 | "--token", |
170 | process.env["WM_TOKEN"] ?? "", |
171 | "--workspace", |
172 | workspace_id, |
173 | "--yes", |
174 | "--base-url", |
175 | process.env["BASE_URL"] + "/", |
176 | message ? "--message" : "", |
177 | message ? message : "" |
178 | ); |
179 | } |
180 |
|
181 | async function wmill_run(secret_position: number, ...cmd: string[]) { |
182 | cmd = cmd.filter((elt) => elt !== ""); |
183 | const cmd2 = cmd.slice(); |
184 | cmd2[secret_position] = "***"; |
185 | console.log(`Running 'wmill ${cmd2.join(" ")} ...'`); |
186 | await wmill.parse(cmd); |
187 | console.log("Command successfully executed"); |
188 | } |
189 |
|