-
Notifications
You must be signed in to change notification settings - Fork 184
ci: upload junod binaries on GitHub Releases (#1220) #1221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| name: Release Binaries | ||
|
|
||
| on: | ||
| release: | ||
| types: [published] | ||
| workflow_dispatch: | ||
| inputs: | ||
| tag: | ||
| description: Release tag to upload binaries for (for example `v30.0.0`) | ||
| required: true | ||
| type: string | ||
|
|
||
| permissions: | ||
| contents: write | ||
|
|
||
| jobs: | ||
| upload-release-binaries: | ||
| runs-on: ubuntu-latest | ||
|
Comment on lines
+16
to
+18
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win Serialize uploads for the same release tag. A published event and a manual backfill can run concurrently for the same tag. Both runs use Also applies to: 52-55 🧰 Tools🪛 zizmor (1.29.0)[info] 17-17: workflow or action definition without a name (anonymous-definition): this job (anonymous-definition) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| steps: | ||
| - name: Resolve release tag | ||
| id: resolve-tag | ||
| env: | ||
| RELEASE_TAG: ${{ github.event.release.tag_name }} | ||
| INPUT_TAG: ${{ inputs.tag }} | ||
| run: | | ||
| tag="${RELEASE_TAG:-$INPUT_TAG}" | ||
| if [ -z "$tag" ]; then | ||
| echo "Release tag is required" | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "release_tag=$tag" >> "$GITHUB_OUTPUT" | ||
|
Comment on lines
+26
to
+32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/release-binaries.yml | sed -n '1,110p'
printf '%s\n' '--- Makefile references ---'
rg -n -C 6 'VERSION|build:' Makefile
printf '%s\n' '--- release tag sources and interpolation ---'
rg -n -C 4 'RELEASE_TAG|INPUT_TAG|release_tag|github\.event|steps\.resolve-tag|gh release' .github/workflows/release-binaries.ymlRepository: CosmosContracts/juno Length of output: 5816 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- repository release tags ---'
git tag --list | python3 - <<'PY'
import sys
tags = [line.rstrip("\n") for line in sys.stdin]
print(f"count={len(tags)}")
for tag in tags[-20:]:
print(repr(tag))
PY
printf '%s\n' '--- Git ref-format probes ---'
python3 - <<'PY'
import subprocess
candidates = [
"v30.0.0",
'v30.0.0"; echo INJECTED #',
"v30.0.0;echo INJECTED",
"release/foo",
"release@candidate",
"release+build",
"release tag",
"release~candidate",
"release^{x}",
"release..candidate",
"release/@{x}",
]
for tag in candidates:
result = subprocess.run(
["git", "check-ref-format", "--allow-onelevel", f"refs/tags/{tag}"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
print(f"{tag!r}: {'valid-ref' if result.returncode == 0 else 'invalid-ref'}")
PY
printf '%s\n' '--- shell interpolation probes ---'
python3 - <<'PY'
import subprocess
template = 'VERSION="${TAG}" printf "VERSION=%s\\n" "$VERSION"'
candidates = [
'v30.0.0',
'v30.0.0"; printf "INJECTED\\n"; #',
'v30.0.0; printf "INJECTED\\n"',
]
for tag in candidates:
script = template.replace("${TAG}", tag)
result = subprocess.run(
["bash", "-c", script],
text=True,
capture_output=True,
)
print(f"tag={tag!r}")
print(f"returncode={result.returncode}")
print(f"stdout={result.stdout!r}")
print(f"stderr={result.stderr!r}")
PYRepository: CosmosContracts/juno Length of output: 813 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- complete build flag definitions ---'
cat -n Makefile | sed -n '1,75p'
printf '%s\n' '--- release/version format references ---'
rg -n -i -C 3 'release tag|tag.*format|version.*regex|^v[0-9]|git tag|semantic.version|semver' \
.github README.md docs Makefile 2>/dev/null || true
printf '%s\n' '--- workflow validation and output handling patterns ---'
rg -n -C 4 'GITHUB_OUTPUT|github.event.release.tag_name|inputs.tag|VERSION=.*steps\.' .githubRepository: CosmosContracts/juno Length of output: 7558 🏁 Script executed: #!/bin/bash
set -eu
printf '%s\n' '--- recent GitHub release tag names ---'
curl -fsSL 'https://api.github.com/repos/CosmosContracts/juno/releases?per_page=30' |
python3 -c '
import json, sys
for item in json.load(sys.stdin):
print(repr(item.get("tag_name")))
'
printf '%s\n' '--- standalone Makefile quoting probe ---'
python3 - <<'PY'
import subprocess
# This models the Makefile's single-quoted -ldflags recipe argument.
template = "printf '%s\\n' '-X path/version.Version=%s'"
for version in ["v30.0.0", "v30.0.0' ; printf INJECTED ; echo '"]:
script = template % version
result = subprocess.run(["bash", "-c", script], text=True, capture_output=True)
print(f"VERSION={version!r}")
print(f"returncode={result.returncode}")
print(f"stdout={result.stdout!r}")
print(f"stderr={result.stderr!r}")
PYRepository: CosmosContracts/juno Length of output: 853 Validate A crafted manual tag can execute shell commands at lines 45 and 55. Environment passing alone is insufficient because Reject invalid release tags before writing 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
|
|
||
| - name: Checkout release tag | ||
| uses: actions/checkout@v4 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
awk '
/uses:[[:space:]]*actions\/(checkout|setup-go)`@/` &&
$0 !~ /@[0-9a-f]{40}([[:space:]]|#|$)/ {
print
bad=1
}
END { exit bad }
' .github/workflows/release-binaries.ymlRepository: CosmosContracts/juno Length of output: 226 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- workflow ---'
cat -n .github/workflows/release-binaries.yml
printf '%s\n' '--- action references ---'
rg -n '^[[:space:]]*uses:|^[[:space:]]*permissions:|persist-credentials|contents:|concurrency:' .github/workflows/release-binaries.ymlRepository: CosmosContracts/juno Length of output: 2122 Pin the action references to immutable commit SHAs.
🧰 Tools🪛 zizmor (1.29.0)[error] 35-35: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) 🤖 Prompt for AI AgentsSource: Linters/SAST tools |
||
| with: | ||
| ref: ${{ steps.resolve-tag.outputs.release_tag }} | ||
|
|
||
| - name: Setup Go | ||
| uses: actions/setup-go@v5 | ||
| with: | ||
| go-version: 1.25.10 | ||
|
|
||
| - name: Build junod | ||
| run: VERSION="${{ steps.resolve-tag.outputs.release_tag }}" LEDGER_ENABLED=false make build | ||
|
|
||
| - name: Create release assets | ||
| run: | | ||
| cp bin/junod junod | ||
| sha256sum junod > junod_sha256.txt | ||
|
|
||
| - name: Upload release assets | ||
| env: | ||
| GH_TOKEN: ${{ github.token }} | ||
| run: gh release upload "${{ steps.resolve-tag.outputs.release_tag }}" junod junod_sha256.txt --clobber | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,7 @@ name: Dispatch Release to juno-std | |
| on: | ||
| release: | ||
| types: [released] | ||
|
|
||
| env: | ||
| JUNO_REPO: "https://github.com/CosmosContracts/juno.git" | ||
| JUNO_DIR: "proto" | ||
|
|
@@ -43,7 +44,7 @@ jobs: | |
|
|
||
| // Determine release_tag and flags based on event type or manual inputs | ||
| const inputs = (context.payload && context.payload.inputs) || {}; | ||
| const releaseTag = inputs.release_tag || ''; | ||
| const releaseTag = inputs.release_tag || context.payload.release?.tag_name || ''; | ||
| const isDraft = String(inputs.is_draft || '').toLowerCase() === 'true'; | ||
| const isPrerelease = String(inputs.is_prerelease || '').toLowerCase() === 'true'; | ||
|
Comment on lines
+47
to
49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
rg -n -C 4 'releaseTag|isPrerelease|is_prerelease|release\.prerelease|createDispatchEvent' .github/workflowsRepository: CosmosContracts/juno Length of output: 3184 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- workflow context ---'
sed -n '1,145p' .github/workflows/release-dispatch.yml
printf '%s\n' '--- downstream juno-release handlers and payload consumers ---'
rg -n -C 6 'juno-release|is_prerelease|isPrerelease|client_payload|release_tag' .github . 2>/dev/null | head -n 400Repository: CosmosContracts/juno Length of output: 9786 🏁 Script executed: #!/bin/bash
set -euo pipefail
node - <<'JS'
function current(payload) {
const inputs = (payload && payload.inputs) || {};
const releaseTag = inputs.release_tag || payload.release?.tag_name || '';
const isPrerelease = String(inputs.is_prerelease || '').toLowerCase() === 'true';
return { releaseTag, isPrerelease };
}
function proposed(payload) {
const inputs = (payload && payload.inputs) || {};
const releaseTag = inputs.release_tag || payload.release?.tag_name || '';
const hasManualPrerelease = inputs.is_prerelease !== undefined &&
inputs.is_prerelease !== null && inputs.is_prerelease !== '';
const isPrerelease = hasManualPrerelease
? String(inputs.is_prerelease).toLowerCase() === 'true'
: Boolean(payload.release?.prerelease);
return { releaseTag, isPrerelease };
}
const event = { release: { tag_name: 'v1.2.3', prerelease: true } };
const manual = { inputs: { release_tag: 'v1.2.3', is_prerelease: 'false' } };
console.log(JSON.stringify({
releaseEvent: { current: current(event), proposed: proposed(event) },
manualOverride: { current: current(manual), proposed: proposed(manual) }
}, null, 2));
JS
printf '%s\n' '--- local downstream workflow files ---'
git ls-files | rg '(^|/)\.github/workflows/|release|dispatch' | head -n 300
rg -n -C 5 'juno-release|is_prerelease|client_payload|release_tag' --glob '! .git/**' . 2>/dev/null | head -n 300Repository: CosmosContracts/juno Length of output: 927 🌐 Web query:
💡 Result: Based on your query, there appears to be a conflation between two distinct projects [1][2][3]: 1. CosmosContracts/juno and juno-std: These relate to the Cosmos-based Juno blockchain, which uses CosmWasm [1][2][4]. The juno-std repository is a Rust-based standard library for Juno's Protobuffers [2][4]. There is no standard GitHub workflow configuration named juno-release or parameter is_prerelease associated with this organization's official public repositories [1][2][5]. 2. Junobuild (juno.build): This is a separate project for serverless functions on the Internet Computer [3][6]. It maintains a GitHub action (junobuild/juno-action) [7][8] used for building and publishing functions [3]. Regarding the specific terms "juno-release" and "is_prerelease": - These are common patterns found in community-created GitHub Actions to handle release logic (e.g., checking if a tag is a prerelease before running a job) [9]. - If you are looking for how to check if a release is a prerelease in a standard GitHub workflow, you typically use the github context [9]. For example: if: github.event.release.prerelease == true If you have a specific workflow file or private repository context where these terms are used, please verify if they are custom implementation details within that repository, as they are not standard identifiers in the CosmosContracts or Junobuild ecosystems [1][3][8]. Citations:
Preserve prerelease status for release events. When 🤖 Prompt for AI Agents |
||
|
|
||
|
|
@@ -66,7 +67,7 @@ jobs: | |
| repo: process.env.COSMOS_SDK_REPO, | ||
| rev: process.env.COSMOS_SDK_REV, | ||
| dir: process.env.COSMOS_SDK_DIR, | ||
| exclude_mods: ['cosmos/benchmark', 'cosmos/counter', 'cosmos/epochs', 'cosmos/protocolpool], | ||
| exclude_mods: ['cosmos/benchmark', 'cosmos/counter', 'cosmos/epochs', 'cosmos/protocolpool'], | ||
| }, | ||
| wasmd: { | ||
| name: 'wasm', | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -14,6 +14,20 @@ In the past, some releases have been consensus-breaking but only incremented a m | |||||||||||||||||||||||
| the | ||||||||||||||||||||||||
| **Only patch versions, i.e. `x.x.1 -> x.x.2`, or `3.1.0 -> 3.1.1` are guaranteed to be non-consensus breaking.** | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ## Obtaining binaries | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| When `.github/workflows/release-binaries.yml` runs for a published GitHub release, it uploads two release assets for the tag: | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - `junod` | ||||||||||||||||||||||||
| - `junod_sha256.txt` | ||||||||||||||||||||||||
|
Comment on lines
+19
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win Document checksum verification. After downloading both assets from the same release, instruct operators to verify the binary before installation. Proposed documentation - `junod`
- `junod_sha256.txt`
+
+After downloading both files into the same directory, verify the binary:
+
+```sh
+sha256sum -c junod_sha256.txt
+```📝 Committable suggestion
Suggested change
🧰 Tools🪛 LanguageTool[uncategorized] ~19-~19: The official name of this software platform is spelled with a capital “H”. (GITHUB) 🤖 Prompt for AI Agents |
||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| For the existing `v30.0.0` release in `CosmosContracts/juno#1220`, maintainers can backfill those assets by running the `Release Binaries` workflow manually with `tag=v30.0.0`. | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| Until that workflow has run for `v30.0.0`, use one of these install paths instead: | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| - Docker: `ghcr.io/cosmoscontracts/juno:v30.0.0` | ||||||||||||||||||||||||
| - Source install: `git checkout v30.0.0 && make install` | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| ## Scheduled upgrade via governance | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
| For a SoftwareUpgradeProposal via governance: | ||||||||||||||||||||||||
|
|
||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
Repository: CosmosContracts/juno
Length of output: 3014
🏁 Script executed:
Repository: CosmosContracts/juno
Length of output: 3604
🏁 Script executed:
Repository: CosmosContracts/juno
Length of output: 18407
🌐 Web query:
GitHub Actions actions/checkout persist-credentials default GITHUB_TOKEN workflow expression injection run shell official documentation💡 Result:
The actions/checkout action, which is used to check out repositories in GitHub Actions, has a configuration option called persist-credentials. By default, this option is set to true [1][2][3][4]. When persist-credentials is set to true (the default), the action configures the local Git environment with the GITHUB_TOKEN (or an SSH key, if provided) [1][2][4]. This allows subsequent steps in the same workflow job to perform authenticated Git commands, such as git fetch or git push, without needing to explicitly manage authentication [1][4]. While recent versions of actions/checkout have improved security by storing these credentials in a temporary file under$RUNNER_TEMP rather than directly in.git/config, the token remains accessible to any script or action executed within that specific job [1][4]. If an attacker can inject code into the job, they may potentially access this token [5][6][7]. Workflow expression injection is a distinct, critical security vulnerability that occurs when untrusted input (such as pull request titles, issue comments, or branch names) is evaluated within a GitHub Actions expression context ($ {{... }}) and then used directly in a run shell command [8][9][10]. If the workflow uses the expression syntax directly inside a run block, an attacker can escape the intended command string and execute arbitrary commands on the runner [8][7]. To prevent workflow expression injection and protect credentials: 1. Never use expression syntax (${{... }}) directly within run commands [9][7]. 2. Always assign untrusted input to an intermediate environment variable, and then reference that environment variable using the standard syntax of the shell (e.g., "$VAR" in Bash) [9][7][11][10]. 3. Set persist-credentials: false in your actions/checkout step if your workflow does not require authenticated Git operations, as this prevents the token from being made available to the local Git configuration [1][5]. 4. Follow the principle of least privilege by explicitly defining permissions for your GITHUB_TOKEN at the job or workflow level, ensuring it only has the permissions necessary for the task [6][9][10].
Citations:
persist-credentialsor change the default tofalseactions/checkout#485🌐 Web query:
GitHub Actions concurrency workflow jobs official documentation release upload💡 Result:
In GitHub Actions, you can control the execution of workflows and jobs using the
concurrencykey [1][2][3]. This allows you to limit concurrent runs, which is especially useful for tasks like deployments or releases where parallel execution could cause conflicts or corrupt states [4][5][6]. Key Concepts and Configuration: 1. Concurrency Groups: By defining aconcurrencykey, you assign a workflow or job to a specific group [1][2]. When multiple runs share the same group name, GitHub Actions ensures that only one such job or workflow is in progress at any given time [1][3]. Group names can be hard-coded strings or dynamic expressions (e.g.,${{ github.workflow }}-${{ github.ref }}) [1][2]. 2. Scope: You can defineconcurrencyat the workflow level or the individual job level [1][3]. Note that workflow-level concurrency can only use contexts available at that scope, such asgithub,inputs, orvars, whereas job-level concurrency can access more specific contexts likematrixorneeds[1][7]. 3. Handling Pending Jobs: - Default Behavior: By default, if a new run starts while another with the same concurrency group is in progress, the new run is marked aspending[1][4]. Any existingpendingrun in that group is automatically canceled [1][8][2]. - Cancel-in-progress: You can explicitly setcancel-in-progress: trueto cancel the currently running job instead of just the pending one, which is a common pattern to ensure only the latest commit triggers a deployment [5][6]. - Queuing: If you prefer that runs execute sequentially rather than being canceled, you can use thequeueproperty (e.g.,queue: max) to allow multiple runs to wait in a queue [1][9][2]. Up to 100 runs can be queued per concurrency group [9][10]. Common Pitfalls: - Overly Unique Keys: Do not includegithub.shaorgithub.run_idin your concurrency group key [5][6]. These values are unique to every single run, which prevents jobs from ever sharing a concurrency group and effectively disables the feature [5][6]. - Incorrect Contexts: Attempting to use a job-specific context (likematrix) in a workflow-levelconcurrencyblock will result in an error [7]. Always ensure the contexts used in your expression are available at the chosen scope [7].Citations:
🏁 Script executed:
Repository: CosmosContracts/juno
Length of output: 1431
🏁 Script executed:
Repository: CosmosContracts/juno
Length of output: 687
🏁 Script executed:
Repository: CosmosContracts/juno
Length of output: 647
Separate the build from the release upload and harden tag handling.
contents: readto the build job andcontents: writeonly to the upload job. Setpersist-credentials: falseon checkout.v30.0.0";id;#executes shell commands at lines 45 and 55. Pass the tag through an environment variable and use"$RELEASE_TAG"in both commands.--clobberuploads.actions/checkoutandactions/setup-goto full commit SHAs.🧰 Tools
🪛 zizmor (1.29.0)
[error] 14-14: overly broad permissions (excessive-permissions): contents: write is overly broad at the workflow level
(excessive-permissions)
[warning] 14-14: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment
(undocumented-permissions)
🤖 Prompt for AI Agents
Source: Linters/SAST tools