Add upload-assets safe output to python-dataviz workflow#2911
Conversation
- Added upload-assets to python-data-charts workflow - Removed workflow_dispatch inputs (data_source, chart_type) - Simplified instructions to generate random data instead of specific data sources - Added instructions for uploading charts as assets and embedding images in reports - Updated shared python-dataviz.md with asset upload documentation Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
Agentic Changeset Generator triggered by this pull request. |
There was a problem hiding this comment.
Pull Request Overview
This PR adds URL-addressable chart image support to the python-dataviz workflow by introducing the upload-assets safe output feature. The changes simplify the sample workflow to use randomly generated data instead of external sources while enabling AI agents to embed generated visualizations directly in discussion reports.
Key changes:
- Added
upload-assetssafe output configuration to publish charts to an orphaned git branch - Simplified sample workflow to generate random NumPy data instead of requiring GitHub API or external data sources
- Added comprehensive documentation for the 3-step asset upload process in shared workflow
Reviewed Changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
.github/workflows/shared/python-dataviz.md |
Added "Including Images in Reports" documentation section explaining asset upload workflow |
.github/workflows/python-data-charts.md |
Simplified to use random data generation, added upload-assets safe output, removed workflow_dispatch inputs |
.github/workflows/python-data-charts.lock.yml |
Generated workflow YAML with asset upload job, environment variables, and updated permissions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| if (hasChanges) { | ||
| const commitMessage = `[skip-ci] Add ${uploadCount} asset(s)`; | ||
| await exec.exec(`git`, [`commit`, `-m`, `"${commitMessage}"`]); |
There was a problem hiding this comment.
The commit message is double-quoted in the exec call, but the template literal already contains the message. This will result in the commit message being wrapped in literal quote characters. Remove the quotes from the -m parameter: await exec.exec('git', ['commit', '-m', commitMessage]);
| await exec.exec(`git`, [`commit`, `-m`, `"${commitMessage}"`]); | |
| await exec.exec(`git`, [`commit`, `-m`, commitMessage]); |
| await exec.exec(`git rev-parse --verify origin/${normalizedBranchName}`); | ||
| await exec.exec(`git checkout -B ${normalizedBranchName} origin/${normalizedBranchName}`); | ||
| core.info(`Checked out existing branch from origin: ${normalizedBranchName}`); | ||
| } catch (originError) { | ||
| core.info(`Creating new orphaned branch: ${normalizedBranchName}`); | ||
| await exec.exec(`git checkout --orphan ${normalizedBranchName}`); | ||
| await exec.exec(`git rm -rf .`); | ||
| await exec.exec(`git clean -fdx`); |
There was a problem hiding this comment.
Using template literals for shell commands without proper escaping can be vulnerable to injection if normalizedBranchName contains special characters. While normalizeBranchName() sanitizes the input, explicitly passing branch names as separate arguments is safer: await exec.exec('git', ['rev-parse', '--verify', origin/${normalizedBranchName}]);
| await exec.exec(`git rev-parse --verify origin/${normalizedBranchName}`); | |
| await exec.exec(`git checkout -B ${normalizedBranchName} origin/${normalizedBranchName}`); | |
| core.info(`Checked out existing branch from origin: ${normalizedBranchName}`); | |
| } catch (originError) { | |
| core.info(`Creating new orphaned branch: ${normalizedBranchName}`); | |
| await exec.exec(`git checkout --orphan ${normalizedBranchName}`); | |
| await exec.exec(`git rm -rf .`); | |
| await exec.exec(`git clean -fdx`); | |
| await exec.exec('git', ['rev-parse', '--verify', `origin/${normalizedBranchName}`]); | |
| await exec.exec('git', ['checkout', '-B', normalizedBranchName, `origin/${normalizedBranchName}`]); | |
| core.info(`Checked out existing branch from origin: ${normalizedBranchName}`); | |
| } catch (originError) { | |
| core.info(`Creating new orphaned branch: ${normalizedBranchName}`); | |
| await exec.exec('git', ['checkout', '--orphan', normalizedBranchName]); | |
| await exec.exec('git', ['rm', '-rf', '.']); | |
| await exec.exec('git', ['clean', '-fdx']); |
| continue; | ||
| } | ||
| fs.copyFileSync(assetSourcePath, targetFileName); | ||
| await exec.exec(`git add "${targetFileName}"`); |
There was a problem hiding this comment.
Using template literals with quoted filenames in shell commands is error-prone. Pass the filename as a separate argument to avoid issues with special characters: await exec.exec('git', ['add', targetFileName]);
| await exec.exec(`git add "${targetFileName}"`); | |
| await exec.exec('git', ['add', targetFileName]); |
| await exec.exec(`git checkout --orphan ${normalizedBranchName}`); | ||
| await exec.exec(`git rm -rf .`); | ||
| await exec.exec(`git clean -fdx`); |
There was a problem hiding this comment.
These git commands use template literals without proper argument separation. Use array syntax consistently: await exec.exec('git', ['checkout', '--orphan', normalizedBranchName]);, await exec.exec('git', ['rm', '-rf', '.']);, and await exec.exec('git', ['clean', '-fdx']);
| await exec.exec(`git checkout --orphan ${normalizedBranchName}`); | |
| await exec.exec(`git rm -rf .`); | |
| await exec.exec(`git clean -fdx`); | |
| await exec.exec('git', ['checkout', '--orphan', normalizedBranchName]); | |
| await exec.exec('git', ['rm', '-rf', '.']); | |
| await exec.exec('git', ['clean', '-fdx']); |
| await exec.exec(`git rev-parse --verify origin/${normalizedBranchName}`); | ||
| await exec.exec(`git checkout -B ${normalizedBranchName} origin/${normalizedBranchName}`); | ||
| core.info(`Checked out existing branch from origin: ${normalizedBranchName}`); | ||
| } catch (originError) { | ||
| core.info(`Creating new orphaned branch: ${normalizedBranchName}`); | ||
| await exec.exec(`git checkout --orphan ${normalizedBranchName}`); | ||
| await exec.exec(`git rm -rf .`); | ||
| await exec.exec(`git clean -fdx`); |
There was a problem hiding this comment.
Use array argument syntax for git commands to avoid shell injection risks: await exec.exec('git', ['checkout', '-B', normalizedBranchName, origin/${normalizedBranchName}]);
| await exec.exec(`git rev-parse --verify origin/${normalizedBranchName}`); | |
| await exec.exec(`git checkout -B ${normalizedBranchName} origin/${normalizedBranchName}`); | |
| core.info(`Checked out existing branch from origin: ${normalizedBranchName}`); | |
| } catch (originError) { | |
| core.info(`Creating new orphaned branch: ${normalizedBranchName}`); | |
| await exec.exec(`git checkout --orphan ${normalizedBranchName}`); | |
| await exec.exec(`git rm -rf .`); | |
| await exec.exec(`git clean -fdx`); | |
| await exec.exec('git', ['rev-parse', '--verify', `origin/${normalizedBranchName}`]); | |
| await exec.exec('git', ['checkout', '-B', normalizedBranchName, `origin/${normalizedBranchName}`]); | |
| core.info(`Checked out existing branch from origin: ${normalizedBranchName}`); | |
| } catch (originError) { | |
| core.info(`Creating new orphaned branch: ${normalizedBranchName}`); | |
| await exec.exec('git', ['checkout', '--orphan', normalizedBranchName]); | |
| await exec.exec('git', ['rm', '-rf', '.']); | |
| await exec.exec('git', ['clean', '-fdx']); |
| if (isStaged) { | ||
| core.summary.addRaw("## Staged Asset Publication"); | ||
| } else { | ||
| await exec.exec(`git push origin ${normalizedBranchName}`); |
There was a problem hiding this comment.
Use array argument syntax for the git push command: await exec.exec('git', ['push', 'origin', normalizedBranchName]);
| await exec.exec(`git push origin ${normalizedBranchName}`); | |
| await exec.exec('git', ['push', 'origin', normalizedBranchName]); |
The python-dataviz shared workflow needed support for URL-addressable chart images in reports. The sample workflow also required simplification to demonstrate asset uploads with randomly generated data instead of requiring external data sources.
Changes
Sample Workflow (
python-data-charts.md)upload-assetssafe output to enable chart publishing to orphaned git branchworkflow_dispatchinputs (data_source,chart_type)Shared Workflow (
python-dataviz.md)plt.savefig()upload assettool (returns GitHub raw URL)Example Usage
The AI agent now generates visualizations and embeds them directly:
Assets are published to
assets/${{ github.workflow }}branch with default config (PNG/JPG/JPEG, 10MB max).Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.