Skip to content

Commit

Permalink
feat(nx-container): improvements in inferred task
Browse files Browse the repository at this point in the history
  • Loading branch information
gperdomor committed Dec 8, 2024
1 parent b2943a5 commit 38fc8aa
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 42 deletions.
40 changes: 25 additions & 15 deletions plugins/nx-container/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,52 @@ This executor not handle registry login steps, so if you wanna push your contain
Adding the Container plugin to an existing Nx workspace can be done with the following:

```bash
npm install -D @nx-tools/nx-container
nx add @nx-tools/nx-container
```

```bash
yarn add -D @nx-tools/nx-container
```
## Using the Container Plugin

If you want an "automatic" tag management and [OCI Image Format Specification](https://github.com/opencontainers/image-spec/blob/master/annotations.md) for labels, you need to install the optional `@nx-tools/container-metadata` package:
### Configuring an application

```bash
npm install -D @nx-tools/container-metadata
```
It's straightforward to setup your application:

```bash
yarn add -D @nx-tools/container-metadata
#### Using Inferred target (Project Crystal):

It's straightforward to setup your application with 2 simple steps

1. Run this command `nx g @nx-tools/nx-container:init` or add nx-container manually to the plugins array in your `nx.json` file, like is showed below:

```
{
"plugin": "@nx-tools/nx-container",
"options": {
"defaultEngine": "docker",
"defaultRegistry": "docker.io"
}
}
```

> @nx-tools/container-metadata is the succesor of `@nx-tools/docker-metadata`.
2. Add a`Dockerfile` to your application

## Using the Container Plugin
> Note: This requires `@nx-tools/nx-container`verion `6.2.0` or above.
### Configuring an application
#### Manual configuration

It's straightforward to setup your application:
To setup a `container` task, or override inferred task, you can use this command:

```bash
nx g @nx-tools/nx-container:configuration appName
```

By default, the application will be configured with:

- A Dockerfile in the application root.
- A Dockerfile in the application root (Will be created if no Dockerfile is present in app directory)
- A target to build your application using the Docker engine.

We can then build our application with the following command:

### Build your application

```bash
nx container appName
```
Expand Down
2 changes: 1 addition & 1 deletion plugins/nx-container/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@nx-tools/nx-container",
"version": "6.1.1",
"version": "6.2.0",
"author": "gperdomor <[email protected]>",
"repository": "https://github.com/gperdomor/nx-tools",
"bugs": "https://github.com/gperdomor/nx-tools/issues",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const DEFAULT_ENGINE = 'docker';
export const DEFAULT_REGISTRY = 'docker.io';

export const DEFAULT_TEMPLATE = 'empty';
20 changes: 14 additions & 6 deletions plugins/nx-container/src/generators/init/init.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { GeneratorCallback, PluginConfiguration, readNxJson, runTasksInSerial, Tree, updateNxJson } from '@nx/devkit';
import {
ExpandedPluginConfiguration,
GeneratorCallback,
PluginConfiguration,
readNxJson,
runTasksInSerial,
Tree,
updateNxJson,
} from '@nx/devkit';
import { ContainerPluginOptions } from '../../plugins';
import { Schema } from './schema';
import { DEFAULT_ENGINE } from '../configuration/constants';

export async function initGenerator(tree: Tree, options: Schema): Promise<GeneratorCallback> {
const tasks: GeneratorCallback[] = [];
Expand All @@ -10,10 +18,10 @@ export async function initGenerator(tree: Tree, options: Schema): Promise<Genera
if (!hasContainerPlugin(nxJson)) {
nxJson.plugins ??= [];

const pluginConfig: PluginConfiguration =
options.defaultEngine !== DEFAULT_ENGINE
? { plugin: '@nx-tools/nx-container', options: { defaultEngine: options.defaultEngine } }
: '@nx-tools/nx-container';
const pluginConfig: ExpandedPluginConfiguration<ContainerPluginOptions> = {
plugin: '@nx-tools/nx-container',
options: { defaultEngine: options.defaultEngine, defaultRegistry: options.defaultRegistry },
};

nxJson.plugins.push(pluginConfig);
updateNxJson(tree, nxJson);
Expand Down
1 change: 1 addition & 0 deletions plugins/nx-container/src/generators/init/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export interface Schema {
* Provide the default container engine to be used.
*/
defaultEngine?: 'docker' | 'podman' | 'kaniko';
defaultRegistry?: string;
}
6 changes: 6 additions & 0 deletions plugins/nx-container/src/generators/init/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
]
},
"alias": "e"
},
"defaultRegistry": {
"type": "string",
"description": "Provide the default registry to be used.",
"default": "docker.io",
"alias": "r"
}
}
}
54 changes: 34 additions & 20 deletions plugins/nx-container/src/plugins/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,17 @@ import {
TargetConfiguration,
writeJsonFile,
} from '@nx/devkit';
import { dirname, join } from 'path';
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
import { getLockFileName } from '@nx/js';
import { existsSync, readdirSync, readFileSync } from 'fs';
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
import { workspaceDataDirectory } from 'nx/src/utils/cache-directory';
import { DEFAULT_ENGINE } from '../generators/configuration/constants';
import { dirname, join } from 'path';
import { DEFAULT_ENGINE, DEFAULT_REGISTRY } from '../generators/configuration/constants';

export interface ContainerPluginOptions {
buildTargetName?: string;
defaultEngine?: string;
defaultRegistry?: string;
}

const cachePath = join(workspaceDataDirectory, 'container.hash');
Expand All @@ -41,17 +42,17 @@ export const createNodes: CreateNodes<ContainerPluginOptions> = [
options = normalizeOptions(options);
const projectRoot = dirname(configFilePath);

// Do not create a project if project.json isn't there.
// Do not create a project if package.json and project.json isn't there.
const siblingFiles = readdirSync(join(context.workspaceRoot, projectRoot));
if (!siblingFiles.includes('project.json')) {
if (!siblingFiles.includes('package.json') && !siblingFiles.includes('project.json')) {
return {};
}

const hash = await calculateHashForCreateNodes(projectRoot, options, context, [
getLockFileName(detectPackageManager(context.workspaceRoot)),
]);

const projectName = buildProjectName(projectRoot, context.workspaceRoot);
const projectName = buildProjectName(projectRoot, context.workspaceRoot, options);

targetsCache[hash] ??= buildTargets(projectRoot, options, projectName);

Expand All @@ -72,16 +73,23 @@ function buildTargets(projectRoot: string, options: ContainerPluginOptions, proj
dependsOn: ['build'],
options: {
engine: options.defaultEngine,
metadata: {
images: [projectName],
load: true,
tags: [
'type=schedule',
'type=ref,event=branch',
'type=ref,event=tag',
'type=ref,event=pr',
'type=sha,prefix=sha-',
],
tags: [`${projectName}:dev`],
load: true,
},
configurations: {
ci: {
load: false,
push: true,
metadata: {
images: [projectName],
tags: [
'type=schedule',
'type=ref,event=branch',
'type=ref,event=tag',
'type=ref,event=pr',
'type=sha,prefix=sha-',
],
},
},
},
},
Expand All @@ -90,23 +98,29 @@ function buildTargets(projectRoot: string, options: ContainerPluginOptions, proj
return targets;
}

function buildProjectName(projectRoot: string, workspaceRoot: string): string | undefined {
function buildProjectName(
projectRoot: string,
workspaceRoot: string,
options: ContainerPluginOptions
): string | undefined {
const packageJsonPath = join(workspaceRoot, projectRoot, 'package.json');
const projectJsonPath = join(workspaceRoot, projectRoot, 'project.json');
let name: string;
const registry = options.defaultRegistry.length ? `${options.defaultRegistry}/` : '';
if (existsSync(projectJsonPath)) {
const projectJson = parseJson(readFileSync(projectJsonPath, 'utf-8'));
name = projectJson.name;
name = `${registry}${projectJson.name}`;
} else if (existsSync(packageJsonPath)) {
const packageJson = parseJson(readFileSync(packageJsonPath, 'utf-8'));
name = packageJson.name;
name = `${registry}${packageJson.name}`;
}
return name;
return name.replace(/@/gi, '');
}

function normalizeOptions(options: ContainerPluginOptions): ContainerPluginOptions {
options ??= {};
options.buildTargetName ??= 'container';
options.defaultEngine ??= DEFAULT_ENGINE;
options.defaultRegistry ??= DEFAULT_REGISTRY;
return options;
}

0 comments on commit 38fc8aa

Please sign in to comment.