mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-31 06:21:11 +00:00 
			
		
		
		
	fix!: use run ID instead of run Index in artifacts download web views
- the run ID used to download artifacts is absolute (ID) instead of being relative to the repository (Index) for compatibility with the url built and returned as `artifact-url` by the the upload-artifact@v4 action. - this is a breaking change because URLs to download artifacts previous saved/bookmarked and not yet expired expired are no longer working, they need to be looked up again by visiting the job web page. - add unit tests for getRunByID(). - RepoActionView.test.js verifies the download URL is built using the run ID. - lAdd integration tests to verify the RunID is set as expected in the template used by RepoActionView.vue. Refs https://code.forgejo.org/forgejo/runner/issues/187
This commit is contained in:
		
					parent
					
						
							
								f7b0eb16c8
							
						
					
				
			
			
				commit
				
					
						b047a60a09
					
				
			
		
					 9 changed files with 233 additions and 19 deletions
				
			
		|  | @ -215,3 +215,104 @@ test('load multiple steps on a finished action', async () => { | |||
|   expect(wrapper.get('.job-step-section:nth-of-type(2) .job-log-line:nth-of-type(2) .log-msg').text()).toEqual('Step #2 Log #2'); | ||||
|   expect(wrapper.get('.job-step-section:nth-of-type(2) .job-log-line:nth-of-type(3) .log-msg').text()).toEqual('Step #2 Log #3'); | ||||
| }); | ||||
| 
 | ||||
| test('artifacts download links', async () => { | ||||
|   Object.defineProperty(document.documentElement, 'lang', {value: 'en'}); | ||||
|   vi.spyOn(global, 'fetch').mockImplementation((url, opts) => { | ||||
|     if (url.endsWith('/artifacts')) { | ||||
|       return Promise.resolve({ | ||||
|         ok: true, | ||||
|         json: vi.fn().mockResolvedValue( | ||||
|           { | ||||
|             artifacts: [ | ||||
|               {name: 'artifactname1', size: 111, status: 'completed'}, | ||||
|               {name: 'artifactname2', size: 222, status: 'expired'}, | ||||
|             ], | ||||
|           }, | ||||
|         ), | ||||
|       }); | ||||
|     } | ||||
| 
 | ||||
|     const postBody = JSON.parse(opts.body); | ||||
|     const stepsLog_value = []; | ||||
|     for (const cursor of postBody.logCursors) { | ||||
|       if (cursor.expanded) { | ||||
|         stepsLog_value.push( | ||||
|           { | ||||
|             step: cursor.step, | ||||
|             cursor: 0, | ||||
|             lines: [ | ||||
|               {index: 1, message: `Step #${cursor.step + 1} Log #1`, timestamp: 0}, | ||||
|             ], | ||||
|           }, | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
|     const jobs_value = { | ||||
|       state: { | ||||
|         run: { | ||||
|           status: 'success', | ||||
|           commit: { | ||||
|             pusher: {}, | ||||
|           }, | ||||
|         }, | ||||
|         currentJob: { | ||||
|           steps: [ | ||||
|             { | ||||
|               summary: 'Test Step #1', | ||||
|               duration: '1s', | ||||
|               status: 'success', | ||||
|             }, | ||||
|           ], | ||||
|         }, | ||||
|       }, | ||||
|       logs: { | ||||
|         stepsLog: opts.body?.includes('"cursor":null') ? stepsLog_value : [], | ||||
|       }, | ||||
|     }; | ||||
| 
 | ||||
|     return Promise.resolve({ | ||||
|       ok: true, | ||||
|       json: vi.fn().mockResolvedValue( | ||||
|         jobs_value, | ||||
|       ), | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   const wrapper = mount(RepoActionView, { | ||||
|     props: { | ||||
|       actionsURL: 'https://example.com/example-org/example-repo/actions', | ||||
|       runIndex: '10', | ||||
|       runID: '1001', | ||||
|       jobIndex: '2', | ||||
|       locale: { | ||||
|         approve: '', | ||||
|         cancel: '', | ||||
|         rerun: '', | ||||
|         artifactsTitle: 'artifactTitleHere', | ||||
|         areYouSure: '', | ||||
|         confirmDeleteArtifact: '', | ||||
|         rerun_all: '', | ||||
|         showTimeStamps: '', | ||||
|         showLogSeconds: '', | ||||
|         showFullScreen: '', | ||||
|         downloadLogs: '', | ||||
|         status: { | ||||
|           unknown: '', | ||||
|           waiting: '', | ||||
|           running: '', | ||||
|           success: '', | ||||
|           failure: '', | ||||
|           cancelled: '', | ||||
|           skipped: '', | ||||
|           blocked: '', | ||||
|         }, | ||||
|       }, | ||||
|     }, | ||||
|   }); | ||||
|   await flushPromises(); | ||||
| 
 | ||||
|   expect(wrapper.get('.job-artifacts .job-artifacts-title').text()).toEqual('artifactTitleHere'); | ||||
|   expect(wrapper.get('.job-artifacts .job-artifacts-item:nth-of-type(1) .job-artifacts-link').attributes('href')).toEqual('https://example.com/example-org/example-repo/actions/runs/1001/artifacts/artifactname1'); | ||||
|   expect(wrapper.get('.job-artifacts .job-artifacts-item:nth-of-type(2) .job-artifacts-link').attributes('href')).toEqual('https://example.com/example-org/example-repo/actions/runs/1001/artifacts/artifactname2'); | ||||
| }); | ||||
|  |  | |||
|  | @ -15,6 +15,7 @@ const sfc = { | |||
|   }, | ||||
|   props: { | ||||
|     runIndex: String, | ||||
|     runID: String, | ||||
|     jobIndex: String, | ||||
|     actionsURL: String, | ||||
|     workflowName: String, | ||||
|  | @ -386,6 +387,7 @@ export function initRepositoryActionView() { | |||
| 
 | ||||
|   const view = createApp(sfc, { | ||||
|     runIndex: el.getAttribute('data-run-index'), | ||||
|     runID: el.getAttribute('data-run-id'), | ||||
|     jobIndex: el.getAttribute('data-job-index'), | ||||
|     actionsURL: el.getAttribute('data-actions-url'), | ||||
|     workflowName: el.getAttribute('data-workflow-name'), | ||||
|  | @ -473,7 +475,7 @@ export function initRepositoryActionView() { | |||
|           </div> | ||||
|           <ul class="job-artifacts-list"> | ||||
|             <li class="job-artifacts-item" v-for="artifact in artifacts" :key="artifact.name"> | ||||
|               <a class="job-artifacts-link" target="_blank" :href="run.link+'/artifacts/'+artifact.name"> | ||||
|               <a class="job-artifacts-link" target="_blank" :href="actionsURL+'/runs/'+runID+'/artifacts/'+artifact.name"> | ||||
|                 <SvgIcon name="octicon-file" class="ui text black job-artifacts-icon"/>{{ artifact.name }} | ||||
|               </a> | ||||
|               <a v-if="run.canDeleteArtifact" @click="deleteArtifact(artifact.name)" class="job-artifacts-delete"> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue