Skip to main content

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

  1. 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.
  2. Comment workflow — triggers via workflow_run when 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:

  1. Add the setup job that reads membrowse-targets.json (see Multiple Targets)
  2. Replace the single analyze job with the matrix analyze pattern
  3. 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).

InputRequiredDescription
json_filesNoSpace-separated list of JSON report file paths or glob pattern (e.g., reports/*.json). Used in file mode.
api_keyNoMemBrowse API key. When provided with commit, enables summary mode — fetches data from the API instead of reading JSON files.
commitNoCommit SHA to fetch summary for. Requires api_key.
pr_numberNoPR number to post comment on. Auto-detected from GitHub event context or commit SHA if not provided.
api_urlNoMemBrowse API base URL (default: https://api.membrowse.com)
comment_templateNoPath 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:

  1. Explicit pr_number input — if you pass it directly
  2. GitHub event context — from github.event.pull_request.number (direct pull_request trigger)
  3. MemBrowse API — the /summary response includes the pr_number from the upload metadata
  4. 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

  1. Verify the workflow_run trigger references the exact workflow name: MemBrowse Memory Report
  2. Check that GITHUB_TOKEN has pull-requests: write permission
  3. Ensure artifact names start with membrowse-report
  4. 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.