Add CI workflow and composite setup action#2
Conversation
Introduces a GitHub Actions workflow for CI, including steps for dependency installation, formatting, building, and testing. Adds a reusable composite action to install Erlang, rebar3, and Gleam with caching and fallback installation strategies.
There was a problem hiding this comment.
Pull Request Overview
This PR introduces a GitHub Actions CI workflow for a Gleam project, along with a reusable composite action for setting up the build environment. The workflow performs standard CI tasks (formatting checks, building, testing) while the composite action handles installation of Erlang, rebar3, and Gleam with multiple fallback strategies.
- Adds a comprehensive CI workflow with dependency installation, formatting checks, building, and testing
- Creates a composite action for environment setup with intelligent fallback mechanisms for Gleam installation
- Implements caching strategies for both Gleam dependencies and build artifacts
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
.github/workflows/ci.yml |
Defines the main CI workflow with steps for checkout, environment setup, dependency installation, formatting checks, build, tests, and artifact upload |
.github/actions/ci-setup/action.yml |
Provides a reusable composite action for installing Erlang/OTP, rebar3, and Gleam with multiple fallback installation strategies including binary download and building from source |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| run: | | ||
| set -eux | ||
| mkdir -p ci_artifacts | ||
| echo "tests: ok" > ci_artifacts/test-success.txt |
There was a problem hiding this comment.
The artifact step always creates test-success.txt with "tests: ok" even if the tests actually failed. Since if: always() is used, this file will be created and uploaded regardless of test outcome, which could be misleading. Consider only creating this file when tests succeed, or include the actual test status in the artifact.
| - name: Cache Gleam deps and build | ||
| uses: actions/cache@v4 | ||
| with: | ||
| path: | | ||
| ~/.cache/gleam | ||
| ~/.gleam | ||
| ./_gleam_deps | ||
| ./build | ||
| key: ${{ runner.os }}-gleam-1.13.0-otp28-cache-${{ hashFiles('**/gleam.toml') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-gleam-1.13.0-otp28-cache- | ||
|
|
There was a problem hiding this comment.
The cache paths include both ~/.cache/gleam (line 27 in ci-setup action) and ~/.cache/gleam (line 27 in workflow), creating duplicate caching. Additionally, the cache in the workflow includes more paths and has version/OTP-specific keys, while the action's cache has a simpler key. This could lead to cache conflicts or inefficiency. Consider consolidating the caching strategy to either the workflow or the action, not both.
| - name: Cache Gleam deps and build | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cache/gleam | |
| ~/.gleam | |
| ./_gleam_deps | |
| ./build | |
| key: ${{ runner.os }}-gleam-1.13.0-otp28-cache-${{ hashFiles('**/gleam.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gleam-1.13.0-otp28-cache- |
| ~/.gleam | ||
| ./_gleam_deps | ||
| ./build | ||
| key: ${{ runner.os }}-gleam-1.13.0-otp28-cache-${{ hashFiles('**/gleam.toml') }} |
There was a problem hiding this comment.
The cache key hardcodes gleam-1.13.0-otp28 but the actual versions are specified in the setup step inputs (lines 19-21). If those input values change, the cache key will become stale and won't match. Consider using ${{ inputs.gleam-version }} or deriving the key from the setup inputs to ensure consistency.
|
|
||
| if [ -n "${DOWNLOAD_URL}" ]; then | ||
| echo "Found asset: ${DOWNLOAD_URL}" | ||
| curl -fsSL -o /tmp/gleam.tar.gz "${DOWNLOAD_URL}" |
There was a problem hiding this comment.
The Gleam binary is downloaded via curl without verifying checksums or signatures. This could pose a security risk if the download is compromised or redirected. Consider verifying the downloaded artifact against published checksums or signatures from the Gleam releases page.
| curl -fsSL -o /tmp/gleam.tar.gz "${DOWNLOAD_URL}" | |
| curl -fsSL -o /tmp/gleam.tar.gz "${DOWNLOAD_URL}" | |
| # Download and verify checksum | |
| CHECKSUM_URL="${DOWNLOAD_URL}.sha256" | |
| echo "Downloading checksum from ${CHECKSUM_URL}" | |
| curl -fsSL -o /tmp/gleam.tar.gz.sha256 "${CHECKSUM_URL}" | |
| EXPECTED_SUM=$(cat /tmp/gleam.tar.gz.sha256 | awk '{print $1}') | |
| ACTUAL_SUM=$(sha256sum /tmp/gleam.tar.gz | awk '{print $1}') | |
| if [ "$EXPECTED_SUM" != "$ACTUAL_SUM" ]; then | |
| echo "Checksum verification failed!" | |
| echo "Expected: $EXPECTED_SUM" | |
| echo "Actual: $ACTUAL_SUM" | |
| exit 1 | |
| fi | |
| echo "Checksum verified." |
| if [ -n "${DOWNLOAD_URL}" ]; then | ||
| echo "Found asset: ${DOWNLOAD_URL}" | ||
| curl -fsSL -o /tmp/gleam.tar.gz "${DOWNLOAD_URL}" | ||
| tar -xzf /tmp/gleam.tar.gz -C /usr/local/bin --strip-components=1 |
There was a problem hiding this comment.
The tar command attempts to extract to /usr/local/bin which typically requires root permissions. On GitHub Actions runners, this will likely fail with a permission denied error. Consider using sudo tar or extracting to a user-writable location like $HOME/.local/bin or $HOME/.cargo/bin and ensuring it's in the PATH.
| tar -xzf /tmp/gleam.tar.gz -C /usr/local/bin --strip-components=1 | |
| mkdir -p "$HOME/.local/bin" | |
| tar -xzf /tmp/gleam.tar.gz -C "$HOME/.local/bin" --strip-components=1 | |
| export PATH="$HOME/.local/bin:$PATH" |
| tmpdir=$(mktemp -d) | ||
| git clone --depth 1 --branch "${TAG}" https://github.com/gleam-lang/gleam.git "$tmpdir" | ||
| cd "$tmpdir" | ||
| cargo build --release |
There was a problem hiding this comment.
The fallback build copies the binary to $HOME/.cargo/bin/ but doesn't ensure this directory exists first. If rustup was just installed, the directory should exist, but if cargo was already present, this path might not exist. Consider adding mkdir -p "$HOME/.cargo/bin" before the copy operation to ensure it succeeds.
| cargo build --release | |
| cargo build --release | |
| mkdir -p "$HOME/.cargo/bin" |
| cp target/release/gleam "$HOME/.cargo/bin/" | ||
| } | ||
|
|
||
| export PATH="$HOME/.cargo/bin:$PATH" |
There was a problem hiding this comment.
The PATH is exported within the script but these exports only affect the current shell session. In GitHub Actions composite actions, PATH modifications need to be persisted to subsequent steps using echo "$HOME/.cargo/bin" >> $GITHUB_PATH. Without this, the Gleam binary installed via cargo will not be accessible in the parent workflow's subsequent steps.
| export PATH="$HOME/.cargo/bin:$PATH" | |
| export PATH="$HOME/.cargo/bin:$PATH" | |
| echo "$HOME/.cargo/bin" >> $GITHUB_PATH |
| ./build | ||
| key: ${{ runner.os }}-gleam-1.13.0-otp28-cache-${{ hashFiles('**/gleam.toml') }} | ||
| restore-keys: | | ||
| ${{ runner.os }}-gleam-1.13.0-otp28-cache- |
There was a problem hiding this comment.
The cache restore-keys hardcode gleam-1.13.0-otp28 but the actual versions are specified in the setup step inputs. If those input values change, the restore keys will become stale. Consider using the actual version values from the setup inputs for consistency.
| # Install rustup if necessary | ||
| if ! command -v cargo >/dev/null 2>&1; then | ||
| echo "Installing rustup/cargo" | ||
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y |
There was a problem hiding this comment.
[nitpick] The rustup installation script is piped directly to sh without verification. While this is the official installation method, it poses a security risk if the source is compromised. Consider adding a checksum verification or using a pinned version of the installer script.
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y | |
| # Download rustup installer script | |
| curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o /tmp/rustup-init.sh | |
| # Verify SHA256 hash (update this hash if the installer script changes) | |
| EXPECTED_HASH="REPLACE_WITH_KNOWN_GOOD_HASH" | |
| ACTUAL_HASH="$(sha256sum /tmp/rustup-init.sh | awk '{print $1}')" | |
| if [ "$ACTUAL_HASH" != "$EXPECTED_HASH" ]; then | |
| echo "rustup-init.sh hash mismatch! Aborting." | |
| exit 1 | |
| fi | |
| sh /tmp/rustup-init.sh -y |
Introduces a GitHub Actions workflow for CI, including steps for dependency installation, formatting, building, and testing. Adds a reusable composite action to install Erlang, rebar3, and Gleam with caching and fallback installation strategies.