Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 208 additions & 0 deletions .github/workflows/test-leiningen-jar-download.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
name: Test Leiningen JAR Download

# This workflow tests the fix for issue #128
# https://github.com/DeLaGuardo/setup-clojure/issues/128
#
# Leiningen is moving from GitHub to Codeberg. This workflow verifies that
# setup-clojure downloads the JAR directly from GitHub Releases instead of
# relying on `lein self-install` (which would download from Codeberg for
# newer versions).

permissions:
contents: read

on:
push:
paths:
- "src/leiningen.ts"
- ".github/workflows/test-leiningen-jar-download.yml"
pull_request:
paths:
- "src/leiningen.ts"
- ".github/workflows/test-leiningen-jar-download.yml"
workflow_dispatch:

jobs:
test-leiningen-specific-version:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]
version: ["2.9.1", "2.10.0", "2.11.2", "2.12.0"]

runs-on: ${{ matrix.os }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Prepare java
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "11"

- name: Install Leiningen ${{ matrix.version }}
uses: ./
with:
lein: ${{ matrix.version }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Verify lein version
run: |
echo "Expected version: ${{ matrix.version }}"
lein version
# Verify the version matches (lein version outputs: Leiningen X.Y.Z on Java ...)
lein version 2>&1 | grep -q "Leiningen ${{ matrix.version }}"

- name: Verify LEIN_JAR is set
run: |
echo "LEIN_JAR=$LEIN_JAR"
test -n "$LEIN_JAR" || (echo "LEIN_JAR not set" && exit 1)
test -f "$LEIN_JAR" || (echo "LEIN_JAR file does not exist" && exit 1)

- name: Verify JAR is in self-installs directory
run: |
echo "LEIN_HOME=$LEIN_HOME"
ls -la "$LEIN_HOME/self-installs/"
test -f "$LEIN_HOME/self-installs/leiningen-${{ matrix.version }}-standalone.jar"

test-leiningen-latest:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]

runs-on: ${{ matrix.os }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Prepare java
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "11"

- name: Install Leiningen latest
uses: ./
with:
lein: latest
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Verify lein works
run: lein version

- name: Verify LEIN_JAR is set
run: |
echo "LEIN_JAR=$LEIN_JAR"
test -n "$LEIN_JAR" || (echo "LEIN_JAR not set" && exit 1)
test -f "$LEIN_JAR" || (echo "LEIN_JAR file does not exist" && exit 1)

- name: Verify JAR is in self-installs directory
run: |
echo "LEIN_HOME=$LEIN_HOME"
ls -la "$LEIN_HOME/self-installs/"
# Should have exactly one JAR file
test $(ls "$LEIN_HOME/self-installs/"*.jar | wc -l) -eq 1

test-leiningen-windows:
strategy:
matrix:
version: ["2.11.2", "2.12.0"]

runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Prepare java
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "11"

- name: Install Leiningen ${{ matrix.version }}
uses: ./
with:
lein: ${{ matrix.version }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Verify lein version (PowerShell)
shell: powershell
run: |
lein version
if (-not $env:LEIN_JAR) { throw "LEIN_JAR not set" }
if (-not (Test-Path $env:LEIN_JAR)) { throw "LEIN_JAR file does not exist" }

- name: Verify lein version (cmd)
shell: cmd
run: lein version

- name: Verify JAR location (PowerShell)
shell: powershell
run: |
Write-Host "LEIN_HOME=$env:LEIN_HOME"
Get-ChildItem "$env:LEIN_HOME\self-installs"

test-leiningen-caching:
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Prepare java
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "11"

- name: Install Leiningen (first run - should download)
uses: ./
with:
lein: "2.11.2"
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Verify first installation
run: lein version

- name: Install Leiningen (second run - should use cache)
uses: ./
with:
lein: "2.11.2"
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Verify cached installation
run: lein version

test-leiningen-create-project:
strategy:
matrix:
os: [ubuntu-latest, macOS-latest]

runs-on: ${{ matrix.os }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Prepare java
uses: actions/setup-java@v4
with:
distribution: "zulu"
java-version: "11"

- name: Install Leiningen
uses: ./
with:
lein: "2.12.0"
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Create and test a new project
run: |
cd /tmp
lein new app test-project
cd test-project
# Skip 'lein test' as default app template includes a failing test (FIXME)
lein run
115 changes: 115 additions & 0 deletions __tests__/leiningen.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@ import {VERSION} from '../src/version'
const toolPath = join(__dirname, 'runner', 'tools', 'leiningen')
const tempPath = join(__dirname, 'runner', 'temp', 'leiningen')
const downloadPath = join(__dirname, 'runner', 'download')
const jarDownloadPath = join(__dirname, 'runner', 'download', 'leiningen.jar')
const zipDownloadPath = join(__dirname, 'runner', 'download', 'leiningen.zip')
const cachePath = join(__dirname, 'runner', 'cache')

import * as leiningen from '../src/leiningen'

function httpError(statusCode: number): Error & {httpStatusCode: number} {
return Object.assign(new Error(`Unexpected HTTP response: ${statusCode}`), {
httpStatusCode: statusCode
})
}

jest.mock('@actions/core')
const core: jest.Mocked<typeof _core> = _core as never

Expand Down Expand Up @@ -44,6 +52,7 @@ describe('leiningen tests', () => {
afterEach(async () => {
jest.spyOn(global.Math, 'random').mockRestore()
jest.resetAllMocks()
global.fetch = undefined as never
delete process.env['RUNNER_TOOL_CACHE']
delete process.env['RUNNER_TEMP']
})
Expand All @@ -55,12 +64,21 @@ describe('leiningen tests', () => {
})

it('Install leiningen with normal version', async () => {
// First call downloads lein script, second downloads the JAR
tc.downloadTool.mockResolvedValueOnce(downloadPath)
tc.downloadTool.mockResolvedValueOnce(jarDownloadPath)
fs.stat.mockResolvedValueOnce({isFile: () => true} as never)
tc.cacheDir.mockResolvedValueOnce(cachePath)

await leiningen.setup('2.9.1')

// Verify JAR was downloaded from GitHub releases
expect(tc.downloadTool).toHaveBeenCalledWith(
'https://github.com/technomancy/leiningen/releases/download/2.9.1/leiningen-2.9.1-standalone.jar',
expect.any(String),
undefined
)

expect(io.mkdirP).toHaveBeenNthCalledWith(
1,
join(tempPath, 'temp_2000000000')
Expand All @@ -69,6 +87,22 @@ describe('leiningen tests', () => {
2,
join(tempPath, 'temp_2000000000', 'leiningen', 'bin')
)
// Verify self-installs directory was created
expect(io.mkdirP).toHaveBeenNthCalledWith(
3,
join(tempPath, 'temp_2000000000', 'leiningen', 'self-installs')
)
// Verify JAR was moved to self-installs
expect(io.mv).toHaveBeenCalledWith(
jarDownloadPath,
join(
tempPath,
'temp_2000000000',
'leiningen',
'self-installs',
'leiningen-2.9.1-standalone.jar'
)
)
expect(exec.exec.mock.calls[0]).toMatchObject([
'./lein version',
[],
Expand All @@ -92,12 +126,33 @@ describe('leiningen tests', () => {
})

it('Install latest leiningen', async () => {
// Mock fetch for getting latest version
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: jest.fn().mockResolvedValue({tag_name: '2.12.0'})
})

// First call downloads lein script, second downloads the JAR
tc.downloadTool.mockResolvedValueOnce(downloadPath)
tc.downloadTool.mockResolvedValueOnce(jarDownloadPath)
fs.stat.mockResolvedValueOnce({isFile: () => true} as never)
tc.cacheDir.mockResolvedValueOnce(cachePath)

await leiningen.setup('latest')

// Verify latest version was fetched
expect(global.fetch).toHaveBeenCalledWith(
'https://api.github.com/repos/technomancy/leiningen/releases/latest',
expect.any(Object)
)

// Verify JAR was downloaded with resolved version
expect(tc.downloadTool).toHaveBeenCalledWith(
'https://github.com/technomancy/leiningen/releases/download/2.12.0/leiningen-2.12.0-standalone.jar',
expect.any(String),
undefined
)

expect(io.mkdirP).toHaveBeenNthCalledWith(
1,
join(tempPath, 'temp_2000000000')
Expand Down Expand Up @@ -128,6 +183,66 @@ describe('leiningen tests', () => {
expect(core.addPath).toHaveBeenCalledWith(join(cachePath, 'bin'))
})

it('Falls back to zip artifact when jar returns 404', async () => {
tc.downloadTool.mockResolvedValueOnce(downloadPath)
tc.downloadTool.mockRejectedValueOnce(httpError(404))
tc.downloadTool.mockResolvedValueOnce(zipDownloadPath)
fs.stat.mockResolvedValueOnce({isFile: () => true} as never)
tc.cacheDir.mockResolvedValueOnce(cachePath)

await leiningen.setup('2.9.1')

expect(tc.downloadTool).toHaveBeenNthCalledWith(
2,
'https://github.com/technomancy/leiningen/releases/download/2.9.1/leiningen-2.9.1-standalone.jar',
join(tempPath, 'leiningen-2.9.1-standalone.jar'),
undefined
)
expect(tc.downloadTool).toHaveBeenNthCalledWith(
3,
'https://github.com/technomancy/leiningen/releases/download/2.9.1/leiningen-2.9.1-standalone.zip',
join(tempPath, 'leiningen-2.9.1-standalone.zip'),
undefined
)
expect(io.mv).toHaveBeenCalledWith(
zipDownloadPath,
join(tempPath, 'leiningen-2.9.1-standalone.jar')
)
expect(io.mv).toHaveBeenCalledWith(
join(tempPath, 'leiningen-2.9.1-standalone.jar'),
join(
tempPath,
'temp_2000000000',
'leiningen',
'self-installs',
'leiningen-2.9.1-standalone.jar'
)
)
})

it('Fails when both jar and zip artifacts return 404', async () => {
tc.downloadTool.mockResolvedValueOnce(downloadPath)
tc.downloadTool.mockRejectedValueOnce(httpError(404))
tc.downloadTool.mockRejectedValueOnce(httpError(404))

await expect(leiningen.setup('2.9.1')).rejects.toThrow(
'Unexpected HTTP response: 404'
)

expect(tc.downloadTool).toHaveBeenNthCalledWith(
2,
'https://github.com/technomancy/leiningen/releases/download/2.9.1/leiningen-2.9.1-standalone.jar',
join(tempPath, 'leiningen-2.9.1-standalone.jar'),
undefined
)
expect(tc.downloadTool).toHaveBeenNthCalledWith(
3,
'https://github.com/technomancy/leiningen/releases/download/2.9.1/leiningen-2.9.1-standalone.zip',
join(tempPath, 'leiningen-2.9.1-standalone.zip'),
undefined
)
})

it('Uses version of leiningen installed in cache', async () => {
tc.find.mockReturnValue(cachePath)
await leiningen.setup('2.9.1')
Expand Down
Loading
Loading