Skip to content

Commit

Permalink
feat: Extract git remote into its own option.
Browse files Browse the repository at this point in the history
This makes the usage of git remotes more explicit and easier to
understand. It also allows to combine gitRemote and manifestPath
if the manifest file is not in the root of the git repository.
  • Loading branch information
calavera committed Nov 21, 2024
1 parent 88c9d87 commit 2ca870c
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 49 deletions.
46 changes: 36 additions & 10 deletions src/cargo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,24 @@ import { mkdirSync, existsSync, readFileSync, rmSync } from 'node:fs';
import { join, parse } from 'node:path';
import { tmpdir } from 'os';
import { load } from 'js-toml';
import { BundlingOptions } from './types';
import { exec } from './util';

/**
* Base properties for a Cargo project.
*
* RustFunctionProps and RustExtensionProps cannot inherit from this interface
* because jsii only supports direct inheritance from a single interface.
*/
interface CargoProject {
readonly bundling?: BundlingOptions;
readonly binaryName?: string;
readonly manifestPath?: string;
readonly gitRemote?: string;
readonly gitReference?: string;
readonly gitForceClone?: boolean;
}

export interface Workspace {
members: string[];
}
Expand All @@ -18,30 +34,40 @@ export interface Manifest {
workspace?: Workspace;
}

export function getManifestPath(manifestPath: string, branch?: string, alwaysClone?: boolean): string {
export function getManifestPath(project: CargoProject): string {
const defaultManifestPath = project.manifestPath || 'Cargo.toml';
let manifestPath = defaultManifestPath;

// Determine what type of URL this is and download (git repo) locally
if (isValidGitUrl(manifestPath)) {
if (project.gitRemote && isValidGitUrl(project.gitRemote)) {
const gitReference = project.gitReference || 'HEAD';

// i.e: 3ed81b4751e8f09bfa39fe743ee143df60304db5 HEAD
let latestCommit = exec('git', ['ls-remote', manifestPath, branch ?? 'HEAD']).stdout.toString().split(/(\s+)/)[0];
let latestCommit = exec('git', ['ls-remote', project.gitRemote, gitReference]).stdout.toString().split(/(\s+)/)[0];
const localPath = join(tmpdir(), latestCommit);

if (alwaysClone) {
if (project.gitForceClone) {
rmSync(localPath, { recursive: true, force: true });
}

if (!existsSync(localPath)) {
mkdirSync(localPath, { recursive: true });

const args = ['clone', '--depth', '1', manifestPath, localPath];
if (branch !== undefined) {
args.push('--branch', branch);
const args = ['clone'];
if (gitReference === 'HEAD') {
args.push('--depth', '1');
}

args.push(project.gitRemote, localPath);
exec('git', args);
}

// Append Cargo.toml to the path
manifestPath = join(localPath, 'Cargo.toml');
if (gitReference !== 'HEAD') {
exec('git', ['checkout', gitReference], { cwd: localPath });
}

// Append Cargo.toml to the path
manifestPath = join(localPath, defaultManifestPath);
}
}

let manifestPathResult;
Expand Down
41 changes: 26 additions & 15 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,20 @@ import { LayerVersion, LayerVersionOptions } from 'aws-cdk-lib/aws-lambda';
import { Construct } from 'constructs';
import { Bundling } from './bundling';
import { getManifestPath } from './cargo';
// import { CargoProject } from './cargo';
import { BundlingOptions } from './types';


/**
* Properties for a RustExtension
*/
export interface RustExtensionProps extends LayerVersionOptions {
/**
* Bundling options
*
* @default - use default bundling options
*/
readonly bundling?: BundlingOptions;

/**
* The name of the binary to build, in case that's different than the package's name.
*/
Expand All @@ -14,47 +24,48 @@ export interface RustExtensionProps extends LayerVersionOptions {
/**
* Path to a directory containing your Cargo.toml file, or to your Cargo.toml directly.
*
* This will accept a directory path containing a `Cargo.toml` file, a filepath to your
* `Cargo.toml` file (i.e. `path/to/Cargo.toml`), or a git repository URL
* (e.g. `https://github.com/your_user/your_repo`).
*
* When using a git repository URL, the repository will be cloned to a temporary directory.
* This will accept a directory path containing a `Cargo.toml` file (i.e. `path/to/package`), or a filepath to your
* `Cargo.toml` file (i.e. `path/to/Cargo.toml`). When the `gitRemote` option is provided,
* the `manifestPath` is relative to the root of the git repository.
*
* @default - check the current directory for a `Cargo.toml` file, and throws
* an error if the file doesn't exist.
*/
readonly manifestPath?: string;

/**
* Bundling options
* The git remote URL to clone (e.g `https://github.com/your_user/your_repo`).
*
* @default - use default bundling options
* This repository will be cloned to a temporary directory using `git`.
* The `git` command must be available in the PATH.
*/
readonly bundling?: BundlingOptions;
readonly gitRemote?: string;

/**
* The branch to clone if the `manifestPath` is a git repository.
* The git reference to checkout. This can be a branch, tag, or commit hash.
*
* If this option is not provided, `git clone` will run with the flag `--depth 1`.
*
* @default - the default branch, i.e. HEAD.
*/
readonly branch?: string;
readonly gitReference?: string;

/**
* Always clone the repository if using a git `manifestPath`, even if it has already been
* Always clone the repository if using the `gitRemote` option, even if it has already been
* cloned to the temporary directory.
*
* @default - clones only if the repository and branch does not already exist in the
* @default - clones only if the repository and reference don't already exist in the
* temporary directory.
*/
readonly alwaysClone?: boolean;
readonly gitForceClone?: boolean;
}

/**
* A Lambda extension written in Rust
*/
export class RustExtension extends LayerVersion {
constructor(scope: Construct, resourceName: string, props?: RustExtensionProps) {
const manifestPath = getManifestPath(props?.manifestPath ?? 'Cargo.toml', props?.branch, props?.alwaysClone);
const manifestPath = getManifestPath(props || {});
const bundling = props?.bundling ?? {};

super(scope, resourceName, {
Expand Down
46 changes: 27 additions & 19 deletions src/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,61 +11,69 @@ export { cargoLambdaVersion } from './bundling';
* Properties for a RustFunction
*/
export interface RustFunctionProps extends FunctionOptions {
/**
* The name of the binary to build, in case that's different than the package's name.
*/
readonly binaryName?: string;

/**
* The Lambda runtime to deploy this function.
* `provided.al2023` is the default runtime when this option is not provided.
*/
readonly runtime?: 'provided.al2023' | 'provided.al2';

/**
* Bundling options
*
* @default - use default bundling options
*/
readonly bundling?: BundlingOptions;

/**
* The name of the binary to build, in case that's different than the package's name.
*/
readonly binaryName?: string;

/**
* Path to a directory containing your Cargo.toml file, or to your Cargo.toml directly.
*
* This will accept a directory path containing a `Cargo.toml` file, a filepath to your
* `Cargo.toml` file (i.e. `path/to/Cargo.toml`), or a git repository url
* (e.g. `https://github.com/your_user/your_repo`).
*
* When using a git repository URL, the repository will be cloned to a temporary directory.
* This will accept a directory path containing a `Cargo.toml` file (i.e. `path/to/package`), or a filepath to your
* `Cargo.toml` file (i.e. `path/to/Cargo.toml`). When the `gitRemote` option is provided,
* the `manifestPath` is relative to the root of the git repository.
*
* @default - check the current directory for a `Cargo.toml` file, and throws
* an error if the file doesn't exist.
*/
readonly manifestPath?: string;

/**
* Bundling options
* The git remote URL to clone (e.g `https://github.com/your_user/your_repo`).
*
* @default - use default bundling options
* This repository will be cloned to a temporary directory using `git`.
* The `git` command must be available in the PATH.
*/
readonly bundling?: BundlingOptions;
readonly gitRemote?: string;

/**
* The branch to clone if the `manifestPath` is a git repository.
* The git reference to checkout. This can be a branch, tag, or commit hash.
*
* If this option is not provided, `git clone` will run with the flag `--depth 1`.
*
* @default - the default branch, i.e. HEAD.
*/
readonly branch?: string;
readonly gitReference?: string;

/**
* Always clone the repository if using a git `manifestPath`, even if it has already been
* Always clone the repository if using the `gitRemote` option, even if it has already been
* cloned to the temporary directory.
*
* @default - clones only if the repository and branch does not already exist in the
* @default - clones only if the repository and reference don't already exist in the
* temporary directory.
*/
readonly alwaysClone?: boolean;
readonly gitForceClone?: boolean;
}

/**
* A Rust Lambda function
*/
export class RustFunction extends Function {
constructor(scope: Construct, resourceName: string, props?: RustFunctionProps) {
const manifestPath = getManifestPath(props?.manifestPath ?? 'Cargo.toml', props?.branch, props?.alwaysClone);
const manifestPath = getManifestPath(props || {});

const runtime = new Runtime(props?.runtime || 'provided.al2023');
const bundling = bundlingOptionsFromRustFunctionProps(props);
Expand Down
2 changes: 1 addition & 1 deletion test/bundlingOptions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ describe('bundlingOptionsFromRustFunctionProps', () => {
const forcedDockerBundling = !!env.FORCE_DOCKER_RUN || !cargoLambdaVersion();

const getTestManifestPath = () => {
return getManifestPath(path.join(__dirname, 'fixtures/single-package/Cargo.toml'));
return getManifestPath({ manifestPath: path.join(__dirname, 'fixtures/single-package/Cargo.toml') });
};

const templateWithProps = (props?: RustFunctionProps) => {
Expand Down
8 changes: 4 additions & 4 deletions test/cargo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,22 @@ import { getManifest, getManifestPath } from '../src/cargo';
describe('getManifestPath', () => {
it('works with a path to an existent Cargo.toml file', () => {
const fixture = join(__dirname, 'fixtures/single-package/Cargo.toml');
expect(getManifestPath(fixture)).toEqual(fixture);
expect(getManifestPath({ manifestPath: fixture })).toEqual(fixture);
});

it('works with a base directory', () => {
const fixture = join(__dirname, 'fixtures/single-package');
expect(getManifestPath(fixture)).toEqual(join(fixture, 'Cargo.toml'));
expect(getManifestPath({ manifestPath: fixture })).toEqual(join(fixture, 'Cargo.toml'));
});

it('fails with a different file', () => {
const fixture = join(__dirname, 'fixtures/single-package/src/main.rs');
expect(() => getManifestPath(fixture)).toThrow('manifestPath is specifying a file that is not Cargo.toml');
expect(() => getManifestPath({ manifestPath: fixture })).toThrow('manifestPath is specifying a file that is not Cargo.toml');
});

it('fails with a directory that doesn\'t include a Cargo.toml file', () => {
const fixture = join(__dirname, 'fixtures/single-package/src');
expect(() => getManifestPath(fixture)).toThrow(`'${fixture}/Cargo.toml' is not a path to a Cargo.toml file, use the option \`manifestPath\` to specify the location of the Cargo.toml file`);
expect(() => getManifestPath({ manifestPath: fixture })).toThrow(`'${fixture}/Cargo.toml' is not a path to a Cargo.toml file, use the option \`manifestPath\` to specify the location of the Cargo.toml file`);
});
});

Expand Down

0 comments on commit 2ca870c

Please sign in to comment.