GitLab: push changes to windmill
One script reply has been approved by the moderators Verified

GitLab: push changes to windmill. Setup a webhook in GitLab to trigger this action. It will ignore commits coming from windmill starting with [WM].

Created by hugo697 219 days ago Picked 10 times
Submitted by hugo697 Bun
Verified 193 days ago
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"); // needed in case the asset branch already exists in the repo
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