status_bar.js 7.5 KB
Newer Older
1
const vscode = require('vscode');
2
const openers = require('./openers');
3
const gitLabService = require('./gitlab_service');
4
const { getCurrentWorkspaceFolder } = require('./services/workspace_service');
5 6
const { UserFriendlyError } = require('./errors/user_friendly_error');
const { handleError, logError } = require('./log');
7
const { USER_COMMANDS } = require('./command_names');
8

9 10
const MAXIMUM_DISPLAYED_JOBS = 4;

11
// FIXME: if you are touching this configuration statement, move the configuration to get_extension_configuration.ts
12 13 14 15
const {
  showStatusBarLinks,
  showIssueLinkOnStatusBar,
  showMrStatusOnStatusBar,
K
Kasper Laursen 已提交
16
  showPipelineUpdateNotifications,
17
} = vscode.workspace.getConfiguration('gitlab');
18

19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
const iconForStatus = {
  running: { icon: 'pulse' },
  pending: { icon: 'clock' },
  success: { icon: 'check', text: 'passed' },
  failed: { icon: 'x' },
  canceled: { icon: 'circle-slash' },
  skipped: { icon: 'diff-renamed' },
};

const getStatusText = status => iconForStatus[status].text || status;

const createStatusTextFromJobs = (jobs, status) => {
  let statusText = getStatusText(status);
  const jobNames = jobs.filter(job => job.status === status).map(job => job.name);
  if (jobNames.length > MAXIMUM_DISPLAYED_JOBS) {
    statusText += ' (';
    statusText += jobNames.slice(0, MAXIMUM_DISPLAYED_JOBS).join(', ');
    statusText += `, +${jobNames.length - MAXIMUM_DISPLAYED_JOBS} jobs`;
    statusText += ')';
  } else if (jobNames.length > 0) {
    statusText += ` (${jobNames.join(', ')})`;
  }
  return statusText;
};

44
const createStatusBarItem = (text, command) => {
45
  const statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
F
Fatih Acet 已提交
46 47
  statusBarItem.text = text;
  statusBarItem.show();
48

49 50 51 52
  if (command) {
    statusBarItem.command = command;
  }

53
  return statusBarItem;
F
Fatih Acet 已提交
54
};
55

F
Fatih Acet 已提交
56 57
const commandRegisterHelper = (cmdName, callback) => {
  vscode.commands.registerCommand(cmdName, callback);
F
Fatih Acet 已提交
58
};
F
Fatih Acet 已提交
59

60 61 62 63 64 65 66 67 68 69
class StatusBar {
  constructor() {
    this.pipelineStatusBarItem = null;
    this.pipelinesStatusTimer = null;
    this.mrStatusBarItem = null;
    this.mrIssueStatusBarItem = null;
    this.mrStatusTimer = null;
    this.issue = null;
    this.mr = null;
    this.firstRun = true;
F
Fatih Acet 已提交
70 71
  }

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
  async refreshPipeline() {
    let workspaceFolder = null;
    let project = null;
    let pipeline = null;

    try {
      workspaceFolder = await getCurrentWorkspaceFolder();
      project = await gitLabService.fetchCurrentPipelineProject(workspaceFolder);
      if (project != null) {
        pipeline = await gitLabService.fetchLastPipelineForCurrentBranch(workspaceFolder);
      } else {
        this.pipelineStatusBarItem.hide();
      }
    } catch (e) {
      logError(e);
      if (!project) {
        this.pipelineStatusBarItem.hide();
        return;
90 91 92
      }
    }

93 94 95
    if (pipeline) {
      const { status } = pipeline;
      let statusText = getStatusText(status);
K
Kasper Laursen 已提交
96

97 98 99 100 101
      if (status === 'running' || status === 'failed') {
        try {
          const jobs = await gitLabService.fetchLastJobsForCurrentBranch(pipeline, workspaceFolder);
          if (jobs) {
            statusText = createStatusTextFromJobs(jobs, status);
K
Kasper Laursen 已提交
102
          }
103 104 105 106
        } catch (e) {
          handleError(new UserFriendlyError('Failed to fetch jobs for pipeline.', e));
        }
      }
107

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
      const msg = `$(${iconForStatus[status].icon}) GitLab: Pipeline ${statusText}`;

      if (
        showPipelineUpdateNotifications &&
        this.pipelineStatusBarItem.text !== msg &&
        !this.firstRun
      ) {
        const message = `Pipeline ${statusText}.`;

        vscode.window
          .showInformationMessage(message, { modal: false }, 'View in Gitlab')
          .then(selection => {
            if (selection === 'View in Gitlab') {
              openers.openCurrentPipeline(workspaceFolder);
            }
          });
      }
125

126 127 128 129 130 131 132
      this.pipelineStatusBarItem.text = msg;
      this.pipelineStatusBarItem.show();
    } else {
      this.pipelineStatusBarItem.text = 'GitLab: No pipeline.';
    }
    this.firstRun = false;
  }
133

134 135 136 137 138
  async initPipelineStatus() {
    this.pipelineStatusBarItem = createStatusBarItem(
      '$(info) GitLab: Fetching pipeline...',
      USER_COMMANDS.PIPELINE_ACTIONS,
    );
139

140 141 142
    this.pipelinesStatusTimer = setInterval(() => {
      this.refreshPipeline();
    }, 30000);
F
Fatih Acet 已提交
143

144
    await this.refreshPipeline();
F
Fatih Acet 已提交
145
  }
146

147 148 149
  async fetchMRIssues(workspaceFolder) {
    const issues = await gitLabService.fetchMRIssues(this.mr.iid, workspaceFolder);
    let text = `$(code) GitLab: No issue.`;
150

151 152 153
    if (issues[0]) {
      [this.issue] = issues;
      text = `$(code) GitLab: Issue #${this.issue.iid}`;
154
    }
F
Fatih Acet 已提交
155

156
    this.mrIssueStatusBarItem.text = text;
157
  }
F
Fatih Acet 已提交
158

159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
  async fetchBranchMR() {
    let text = '$(git-pull-request) GitLab: Create MR.';
    let workspaceFolder = null;
    let project = null;

    try {
      workspaceFolder = await getCurrentWorkspaceFolder();
      project = await gitLabService.fetchCurrentProject(workspaceFolder);
      if (project != null) {
        this.mr = await gitLabService.fetchOpenMergeRequestForCurrentBranch(workspaceFolder);
        this.mrStatusBarItem.show();
      } else {
        this.mrStatusBarItem.hide();
      }
    } catch (e) {
      logError(e);
      this.mrStatusBarItem.hide();
    }
177

178 179 180 181 182 183 184
    if (project && this.mr) {
      text = `$(git-pull-request) GitLab: MR !${this.mr.iid}`;
      await this.fetchMRIssues(workspaceFolder);
      this.mrIssueStatusBarItem.show();
    } else if (project) {
      this.mrIssueStatusBarItem.text = `$(code) GitLab: No issue.`;
      this.mrIssueStatusBarItem.show();
F
Fatih Acet 已提交
185
    } else {
186
      this.mrIssueStatusBarItem.hide();
F
Fatih Acet 已提交
187
    }
F
Fatih Acet 已提交
188

189 190
    this.mrStatusBarItem.text = text;
  }
F
Fatih Acet 已提交
191

192 193 194 195 196 197 198 199 200
  async initMrStatus() {
    const cmdName = `gl.mrOpener${Date.now()}`;
    commandRegisterHelper(cmdName, () => {
      if (this.mr) {
        openers.openUrl(this.mr.web_url);
      } else {
        openers.openCreateNewMr();
      }
    });
F
Fatih Acet 已提交
201

202 203 204 205
    this.mrStatusBarItem = createStatusBarItem('$(info) GitLab: Finding MR...', cmdName);
    this.mrStatusTimer = setInterval(() => {
      this.fetchBranchMR();
    }, 60000);
F
Fatih Acet 已提交
206

207 208
    await this.fetchBranchMR();
  }
F
Fatih Acet 已提交
209

210 211 212 213 214 215 216 217 218 219 220
  initMrIssueStatus() {
    const cmdName = `gl.mrIssueOpener${Date.now()}`;
    commandRegisterHelper(cmdName, () => {
      if (this.issue) {
        openers.openUrl(this.issue.web_url);
      } else {
        vscode.window.showInformationMessage(
          'GitLab Workflow: No closing issue found for this MR.',
        );
      }
    });
221

222 223 224 225 226
    this.mrIssueStatusBarItem = createStatusBarItem(
      '$(info) GitLab: Fetching closing issue...',
      cmdName,
    );
  }
227

228 229 230 231 232 233 234 235 236 237 238 239 240
  async init() {
    if (showStatusBarLinks) {
      await this.initPipelineStatus();

      // FIXME: add showMrStatusOnStatusBar to the condition
      // because the initMrStatus() method does all the fetching and initMrIssueStatus
      // only introduces a placeholder item
      if (showIssueLinkOnStatusBar) {
        this.initMrIssueStatus();
      }
      if (showMrStatusOnStatusBar) {
        await this.initMrStatus();
      }
241
    }
242
  }
F
Fatih Acet 已提交
243

244 245 246
  dispose() {
    if (showStatusBarLinks) {
      this.pipelineStatusBarItem.dispose();
247

248 249 250 251 252 253 254
      if (showIssueLinkOnStatusBar) {
        this.mrIssueStatusBarItem.dispose();
      }
      if (showMrStatusOnStatusBar) {
        this.mrStatusBarItem.dispose();
      }
    }
255

256 257 258 259 260 261 262 263 264
    if (this.pipelinesStatusTimer) {
      clearInterval(this.pipelinesStatusTimer);
      this.pipelinesStatusTimer = null;
    }

    if (this.mrStatusTimer) {
      clearInterval(this.mrStatusTimer);
      this.mrStatusTimer = null;
    }
265
  }
266
}
F
Fatih Acet 已提交
267

268 269 270 271
module.exports = {
  StatusBar,
  instance: new StatusBar(),
};