Add log line anchor for action logs (#25532)
Close #24593 Some behavior: - If log step line in hash exists, expand the step and scroll to the log line. - If step exists but line not exists, the step will be expanded. - If step not exists, stays on the job's page. Some Notes: - Changed mounted to async because need to await for first `loadJob` so `currentJobStepsStates` can be initialized and used in `hashChangeListener `. --------- Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
		
							parent
							
								
									36f1fa7792
								
							
						
					
					
						commit
						640a88fa09
					
				| 
						 | 
					@ -204,15 +204,19 @@ const sfc = {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mounted() {
 | 
					  async mounted() {
 | 
				
			||||||
    // load job data and then auto-reload periodically
 | 
					    // load job data and then auto-reload periodically
 | 
				
			||||||
    this.loadJob();
 | 
					    // need to await first loadJob so this.currentJobStepsStates is initialized and can be used in hashChangeListener
 | 
				
			||||||
 | 
					    await this.loadJob();
 | 
				
			||||||
    this.intervalID = setInterval(this.loadJob, 1000);
 | 
					    this.intervalID = setInterval(this.loadJob, 1000);
 | 
				
			||||||
    document.body.addEventListener('click', this.closeDropdown);
 | 
					    document.body.addEventListener('click', this.closeDropdown);
 | 
				
			||||||
 | 
					    this.hashChangeListener();
 | 
				
			||||||
 | 
					    window.addEventListener('hashchange', this.hashChangeListener);
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeUnmount() {
 | 
					  beforeUnmount() {
 | 
				
			||||||
    document.body.removeEventListener('click', this.closeDropdown);
 | 
					    document.body.removeEventListener('click', this.closeDropdown);
 | 
				
			||||||
 | 
					    window.removeEventListener('hashchange', this.hashChangeListener);
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  unmounted() {
 | 
					  unmounted() {
 | 
				
			||||||
| 
						 | 
					@ -280,14 +284,16 @@ const sfc = {
 | 
				
			||||||
      this.fetchPost(`${this.run.link}/approve`);
 | 
					      this.fetchPost(`${this.run.link}/approve`);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    createLogLine(line, startTime) {
 | 
					    createLogLine(line, startTime, stepIndex) {
 | 
				
			||||||
      const div = document.createElement('div');
 | 
					      const div = document.createElement('div');
 | 
				
			||||||
      div.classList.add('job-log-line');
 | 
					      div.classList.add('job-log-line');
 | 
				
			||||||
 | 
					      div.setAttribute('id', `jobstep-${stepIndex}-${line.index}`);
 | 
				
			||||||
      div._jobLogTime = line.timestamp;
 | 
					      div._jobLogTime = line.timestamp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const lineNumber = document.createElement('div');
 | 
					      const lineNumber = document.createElement('a');
 | 
				
			||||||
      lineNumber.className = 'line-num';
 | 
					      lineNumber.classList.add('line-num', 'muted');
 | 
				
			||||||
      lineNumber.textContent = line.index;
 | 
					      lineNumber.textContent = line.index;
 | 
				
			||||||
 | 
					      lineNumber.setAttribute('href', `#jobstep-${stepIndex}-${line.index}`);
 | 
				
			||||||
      div.append(lineNumber);
 | 
					      div.append(lineNumber);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // for "Show timestamps"
 | 
					      // for "Show timestamps"
 | 
				
			||||||
| 
						 | 
					@ -318,7 +324,7 @@ const sfc = {
 | 
				
			||||||
      for (const line of logLines) {
 | 
					      for (const line of logLines) {
 | 
				
			||||||
        // TODO: group support: ##[group]GroupTitle , ##[endgroup]
 | 
					        // TODO: group support: ##[group]GroupTitle , ##[endgroup]
 | 
				
			||||||
        const el = this.getLogsContainer(stepIndex);
 | 
					        const el = this.getLogsContainer(stepIndex);
 | 
				
			||||||
        el.append(this.createLogLine(line, startTime));
 | 
					        el.append(this.createLogLine(line, startTime, stepIndex));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -429,6 +435,21 @@ const sfc = {
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        actionBodyEl.append(fullScreenEl);
 | 
					        actionBodyEl.append(fullScreenEl);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    async hashChangeListener() {
 | 
				
			||||||
 | 
					      const selectedLogStep = window.location.hash;
 | 
				
			||||||
 | 
					      if (!selectedLogStep) return;
 | 
				
			||||||
 | 
					      const [_, step, _line] = selectedLogStep.split('-');
 | 
				
			||||||
 | 
					      if (!this.currentJobStepsStates[step]) return;
 | 
				
			||||||
 | 
					      if (!this.currentJobStepsStates[step].expanded && this.currentJobStepsStates[step].cursor === null) {
 | 
				
			||||||
 | 
					        this.currentJobStepsStates[step].expanded = true;
 | 
				
			||||||
 | 
					        // need to await for load job if the step log is loaded for the first time
 | 
				
			||||||
 | 
					        // so logline can be selected by querySelector
 | 
				
			||||||
 | 
					        await this.loadJob();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      const logLine = this.$refs.steps.querySelector(selectedLogStep);
 | 
				
			||||||
 | 
					      if (!logLine) return;
 | 
				
			||||||
 | 
					      logLine.querySelector('.line-num').click();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -802,10 +823,15 @@ export function initRepositoryActionView() {
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.job-step-section .job-step-logs .job-log-line:hover {
 | 
					.job-log-line:hover,
 | 
				
			||||||
 | 
					.job-log-line:target {
 | 
				
			||||||
  background-color: var(--color-console-hover-bg);
 | 
					  background-color: var(--color-console-hover-bg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.job-log-line:target {
 | 
				
			||||||
 | 
					  scroll-margin-top: 95px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* class names 'log-time-seconds' and 'log-time-stamp' are used in the method toggleTimeDisplay */
 | 
					/* class names 'log-time-seconds' and 'log-time-stamp' are used in the method toggleTimeDisplay */
 | 
				
			||||||
.job-log-line .line-num, .log-time-seconds {
 | 
					.job-log-line .line-num, .log-time-seconds {
 | 
				
			||||||
  width: 48px;
 | 
					  width: 48px;
 | 
				
			||||||
| 
						 | 
					@ -814,6 +840,11 @@ export function initRepositoryActionView() {
 | 
				
			||||||
  user-select: none;
 | 
					  user-select: none;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.job-log-line:target > .line-num {
 | 
				
			||||||
 | 
					  color: var(--color-primary);
 | 
				
			||||||
 | 
					  text-decoration: underline;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.log-time-seconds {
 | 
					.log-time-seconds {
 | 
				
			||||||
  padding-right: 2px;
 | 
					  padding-right: 2px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue