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
8 changes: 8 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ jobs:
- flutter
- flux
- golang
- haskell
- java
- jb
- helm
Expand All @@ -343,6 +344,13 @@ jobs:
- name: aarch64
os: ubuntu-24.04-arm
tag: arm64
# include:
# # no ubuntu arm64 support for haskell
# - lang: haskell
# arch:
# name: x86_64
# os: ubuntu-24.04
# tag: amd64

env:
TAG: ${{ matrix.lang }}
Expand Down
18 changes: 18 additions & 0 deletions docs/custom-registries.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,24 @@ https://dl.google.com/go/go1.21.6.linux-arm64.tar.gz
https://dl.google.com/go/go1.17.5.linux-amd64.tar.gz
```

## `haskell`

Haskell tools are separated.

### `cabal`

Cabal releases are downloaded from:

- `https://downloads.haskell.org/~cabal/`

Samples:

```txt
https://downloads.haskell.org/~cabal/cabal-install-3.16.1.0/cabal-install-3.16.1.0-aarch64-linux-deb10.tar.xz
https://downloads.haskell.org/~cabal/cabal-install-3.16.1.0/cabal-install-3.16.1.0-x86_64-linux-deb10.tar.xz
https://downloads.haskell.org/~cabal/cabal-install-3.16.1.0/SHA256SUMS
```

## `helm`

Helm releases are downloaded from:
Expand Down
4 changes: 4 additions & 0 deletions src/cli/install-tool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { FluxInstallService } from '../tools/flux';
import { GitLfsInstallService } from '../tools/git/lfs';
import { GleamInstallService } from '../tools/gleam';
import { GolangInstallService } from '../tools/golang';
import { CabalInstallService } from '../tools/haskell/cabal';
import { GhcInstallService } from '../tools/haskell/ghc';
import { HelmInstallService } from '../tools/helm';
import { HelmfileInstallService } from '../tools/helmfile';
import {
Expand Down Expand Up @@ -121,6 +123,7 @@ async function prepareInstallContainer(): Promise<Container> {
container.bind(INSTALL_TOOL_TOKEN).to(BazeliskInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(BuildxInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(BunInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(CabalInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(CocoapodsInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(ConanInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(DartInstallService);
Expand All @@ -134,6 +137,7 @@ async function prepareInstallContainer(): Promise<Container> {
container.bind(INSTALL_TOOL_TOKEN).to(FlutterInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(FluxInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(GitLfsInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(GhcInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(GleamInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(GolangInstallService);
container.bind(INSTALL_TOOL_TOKEN).to(GradleInstallService);
Expand Down
4 changes: 4 additions & 0 deletions src/cli/prepare-tool/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { ErlangPrepareService } from '../tools/erlang';
import { ElixirPrepareService } from '../tools/erlang/elixir';
import { FlutterPrepareService } from '../tools/flutter';
import { GolangPrepareService } from '../tools/golang';
import { CabalPrepareService } from '../tools/haskell/cabal';
import { GhcPrepareService } from '../tools/haskell/ghc';
import {
JavaJdkPrepareService,
JavaJrePrepareService,
Expand Down Expand Up @@ -47,13 +49,15 @@ async function prepareContainer(): Promise<Container> {
}

// modern tool services
container.bind(PREPARE_TOOL_TOKEN).to(CabalPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(ConanPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(DartPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(DotnetPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(DockerPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(ElixirPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(ErlangPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(FlutterPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(GhcPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(GolangPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(JavaPrepareService);
container.bind(PREPARE_TOOL_TOKEN).to(JavaJrePrepareService);
Expand Down
73 changes: 73 additions & 0 deletions src/cli/tools/haskell/cabal.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import fs from 'node:fs/promises';
import { join } from 'node:path';
import { injectFromHierarchy, injectable } from 'inversify';
import { BaseInstallService } from '../../install-tool/base-install.service';
import { BasePrepareService } from '../../prepare-tool/base-prepare.service';
import { isFourPartVersion } from '../../utils';

@injectable()
@injectFromHierarchy()
export class CabalPrepareService extends BasePrepareService {
readonly name = 'cabal';
}

@injectable()
@injectFromHierarchy()
export class CabalInstallService extends BaseInstallService {
readonly name = 'cabal';

private get arch(): string {
switch (this.envSvc.arch) {
case 'arm64':
return 'aarch64';
case 'amd64':
return 'x86_64';
}
}

override async install(version: string): Promise<void> {
const baseUrl = `https://downloads.haskell.org/~cabal/cabal-install-${version}/`;
// use static deb10 binary as it is compatible with all supported ubuntu versions
const filename = `cabal-install-${version}-${this.arch}-linux-deb10.tar.xz`;

const checksumFile = await this.http.download({
url: `${baseUrl}SHA256SUMS`,
});
const expectedChecksum = (await fs.readFile(checksumFile, 'utf-8'))
.split('\n')
.find((l) => l.includes(filename))
?.split(' ')[0];

const file = await this.http.download({
url: `${baseUrl}${filename}`,
checksumType: 'sha256',
expectedChecksum,
});

await this.pathSvc.ensureToolPath(this.name);

const path = join(
await this.pathSvc.createVersionedToolPath(this.name, version),
'bin',
);
await fs.mkdir(path);
await this.compress.extract({
file,
cwd: path,
});
}

override validate(version: string): Promise<boolean> {
return Promise.resolve(isFourPartVersion(version));
}

override async link(version: string): Promise<void> {
const src = join(this.pathSvc.versionedToolPath(this.name, version), 'bin');

await this.shellwrapper({ srcDir: src });
}

override async test(_version: string): Promise<void> {
await this._spawn('cabal', ['--version']);
}
}
66 changes: 66 additions & 0 deletions src/cli/tools/haskell/ghc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import fs from 'node:fs/promises';
import { join } from 'node:path';
import { injectFromHierarchy, injectable } from 'inversify';
import { BaseInstallService } from '../../install-tool/base-install.service';
import { BasePrepareService } from '../../prepare-tool/base-prepare.service';

@injectable()
@injectFromHierarchy()
export class GhcPrepareService extends BasePrepareService {
readonly name = 'ghc';
}

@injectable()
@injectFromHierarchy()
export class GhcInstallService extends BaseInstallService {
readonly name = 'ghc';

private get arch(): string {
switch (this.envSvc.arch) {
case 'arm64':
return 'aarch64';
case 'amd64':
return 'x86_64';
}
}

override async install(version: string): Promise<void> {
const baseUrl = `https://downloads.haskell.org/~ghc/${version}/`;
// use static deb10 binary as it is compatible with all supported ubuntu versions
const filename = `ghc-${version}-${this.arch}-deb10-linux.tar.xz`;

const checksumFile = await this.http.download({
url: `${baseUrl}SHA256SUMS`,
});
const expectedChecksum = (await fs.readFile(checksumFile, 'utf-8'))
.split('\n')
.find((l) => l.includes(filename))
?.split(' ')[0];

const file = await this.http.download({
url: `${baseUrl}${filename}`,
checksumType: 'sha256',
expectedChecksum,
});

await this.pathSvc.ensureToolPath(this.name);

const path = await this.pathSvc.createVersionedToolPath(this.name, version);
await this.compress.extract({
file,
cwd: path,
strip: 1,
});
}

override async link(version: string): Promise<void> {
const src = join(this.pathSvc.versionedToolPath(this.name, version), 'bin');

await this.shellwrapper({ srcDir: src });
await this.shellwrapper({ srcDir: src, name: 'ghc-pkg' });
}

override async test(_version: string): Promise<void> {
await this._spawn('ghc', ['--version']);
}
}
8 changes: 7 additions & 1 deletion src/cli/utils/versions.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { describe, expect, test } from 'vitest';
import { isValid, parse } from './versions';
import { isFourPartVersion, isValid, parse } from './versions';

describe('cli/utils/versions', () => {
test('isValid', () => {
expect(isValid('1.0.0')).toBe(true);
expect(isValid('abc')).toBe(false);
});

test('isFourPartVersion', () => {
expect(isFourPartVersion('1.0.0.0')).toBe(true);
expect(isFourPartVersion('1.0.0')).toBe(false);
expect(isFourPartVersion('abc')).toBe(false);
});

test('parse', () => {
expect(parse('1.0.0')).not.toBeNull();
expect(() => parse('abc')).toThrow('Invalid version: abc');
Expand Down
4 changes: 4 additions & 0 deletions src/cli/utils/versions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ export function isValid(version: string): boolean {
return semverValid(version) !== null;
}

export function isFourPartVersion(version: string): boolean {
return /^\d+\.\d+\.\d+\.\d+$/.test(version);
}

export function parse(version: string | undefined): SemVer {
const res = semverParse(version);
if (!res) {
Expand Down
11 changes: 11 additions & 0 deletions test/Dockerfile.distro
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,16 @@ RUN install-tool erlang 28.3.2.0
# renovate: datasource=github-releases packageName=elixir-lang/elixir
RUN install-tool elixir 1.19.5

#--------------------------------------
# Image: test-haskell
#--------------------------------------
FROM build AS test-haskell

# renovate: datasource=github-releases packageName=haskell/cabal versioning=docker
RUN install-tool cabal 3.16.1.0
# renovate: datasource=docker packageName=haskell
RUN install-tool ghc 9.14.1

#--------------------------------------
# Image: test-java
#--------------------------------------
Expand Down Expand Up @@ -242,6 +252,7 @@ FROM base
COPY --from=test /.dummy /.dummy
COPY --from=test-docker /.dummy /.dummy
COPY --from=test-erlang /.dummy /.dummy
COPY --from=test-haskell /.dummy /.dummy
COPY --from=test-java /.dummy /.dummy
COPY --from=test-mono /.dummy /.dummy
COPY --from=test-node /.dummy /.dummy
Expand Down
Loading