Open Source Projects
MemBrowse supports tokenless uploads for open-source projects. When the API key is not available (e.g., on fork PRs where repository secrets are inaccessible), the action automatically falls back to uploading without authentication. MemBrowse recognizes that the upload originates from a GitHub Actions context and matches it to the correct project. Tokenless uploads only work for projects marked as open source in the MemBrowse Portal. The report still reaches the MemBrowse Portal with full analysis.
The standard single-workflow setup from Setup works for:
- Private repositories
- Public repositories where PRs come from branches within the same repo
If your project accepts PRs from external contributors (forks), use the two-workflow setup below. The split is needed because fork PRs cannot write PR comments — the upload itself works fine without secrets.
How It Works
- Report workflow — triggers on PRs and pushes, builds the firmware, analyzes memory, and uploads the report. On fork PRs the API key secret is empty, so the action uses tokenless upload. The report artifact is also saved for the comment workflow.
- Comment workflow — triggers via
workflow_runwhen the report workflow completes. It runs in the context of the base repository with full permissions, downloads the report artifacts, and posts the PR comment.
Report Workflow
Create .github/workflows/membrowse-report.yml:
name: MemBrowse Memory Report
on:
pull_request:
push:
branches:
- main
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
# Add your build tool setup here
- name: Build firmware
run: make all
# continue-on-error ensures the workflow doesn't fail if memory
# analysis has an issue — keeps MemBrowse non-blocking.
- name: Analyze memory
id: analyze
continue-on-error: true
uses: membrowse/membrowse-action@v1
with:
elf: build/firmware.elf
ld: linker.ld
target_name: my-target
api_key: ${{ secrets.MEMBROWSE_API_KEY }}
# linker_vars: "__flash_size__=512K" # Optional: linker variable definitions
- name: Upload report artifact
if: ${{ steps.analyze.outcome == 'success' }}
uses: actions/upload-artifact@v4
with:
name: membrowse-report
path: ${{ steps.analyze.outputs.report_path }}
The api_key is passed as ${{ secrets.MEMBROWSE_API_KEY }}. On pushes to main and same-repo PRs, this authenticates normally. On fork PRs, the secret is empty and the action falls back to tokenless upload — the report still reaches the portal.
Multiple targets
If you use membrowse-targets.json, make these changes to the report workflow:
- Add the
setupjob that readsmembrowse-targets.json(see Multiple Targets) - Replace the single
analyzejob with the matrixanalyzepattern - Use
membrowse-report-${{ matrix.target.name }}for artifact names
The comment workflow needs no changes — its download script filters on artifacts starting with membrowse-report, so it automatically picks up all target reports.
Comment Workflow
Create .github/workflows/membrowse-comment.yml:
name: MemBrowse PR Comment
on:
workflow_run:
workflows: [MemBrowse Memory Report]
types: [completed]
permissions:
contents: read
actions: read
pull-requests: write
jobs:
comment:
runs-on: ubuntu-latest
if: github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success'
steps:
- uses: actions/checkout@v5
# We use github-script instead of download-artifact because the report
# was uploaded by a different workflow run, and download-artifact can
# only access artifacts from the current run.
- name: Download report artifacts
id: download-reports
uses: actions/github-script@v7
with:
result-encoding: string
script: |
const fs = require('fs');
const allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
const reportArtifacts = allArtifacts.data.artifacts.filter(
artifact => artifact.name.startsWith('membrowse-report')
);
if (reportArtifacts.length === 0) {
console.log('No report artifacts found');
return 'skip';
}
fs.mkdirSync('reports', { recursive: true });
for (const artifact of reportArtifacts) {
const download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: artifact.id,
archive_format: 'zip',
});
const zipPath = `${artifact.name}.zip`;
fs.writeFileSync(zipPath, Buffer.from(download.data));
await exec.exec('unzip', ['-o', zipPath, '-d', 'reports']);
}
return 'ok';
- name: Post PR comment
if: steps.download-reports.outputs.result == 'ok'
uses: membrowse/membrowse-action/comment-action@v1
with:
json_files: "reports/*.json"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Action Reference
Inputs (membrowse/membrowse-action/comment-action@v1)
The comment-action supports two modes: file mode (reads local JSON report files) and summary mode (fetches per-target summaries from the MemBrowse API). Use file mode for standard workflows, and summary mode when report artifacts aren't available (e.g., cross-workflow comments).
| Input | Required | Description |
|---|---|---|
json_files | No | Space-separated list of JSON report file paths or glob pattern (e.g., reports/*.json). Used in file mode. |
api_key | No | MemBrowse API key. When provided with commit, enables summary mode — fetches data from the API instead of reading JSON files. |
commit | No | Commit SHA to fetch summary for. Requires api_key. |
pr_number | No | PR number to post comment on. Auto-detected from GitHub event context or commit SHA if not provided. |
api_url | No | MemBrowse API base URL (default: https://api.membrowse.com) |
comment_template | No | Path to a custom Jinja2 template file for comment formatting |
Either json_files or both api_key + commit must be provided.
PR Number Resolution
The comment-action needs to know which PR to post the comment on. It resolves the PR number automatically in this order:
- Explicit
pr_numberinput — if you pass it directly - GitHub event context — from
github.event.pull_request.number(directpull_requesttrigger) - MemBrowse API — the
/summaryresponse includes thepr_numberfrom the upload metadata - GitHub commits API — looks up PRs associated with the commit SHA
For most setups, including fork PRs, step 3 handles it automatically — the PR number is captured during the report upload and returned by the API. No extra configuration is needed.
Troubleshooting
PR comments not appearing
- Verify the
workflow_runtrigger references the exact workflow name:MemBrowse Memory Report - Check that
GITHUB_TOKENhaspull-requests: writepermission - Ensure artifact names start with
membrowse-report - Check the comment workflow logs in the Actions tab — it runs as a separate workflow, so look for "MemBrowse PR Comment" runs
Report workflow succeeds but comment workflow doesn't trigger
The comment workflow only runs when the report workflow completes successfully and was triggered by a pull_request event. Check the if condition in the comment job.