Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6cafdfce1 | ||
|
|
416c2914df | ||
|
|
341bae465f | ||
|
|
264a0eec2a | ||
|
|
545f7cd6ea | ||
|
|
de2888af87 | ||
|
|
43f61228ec | ||
|
|
8d1d731562 | ||
|
|
5706b95a7f | ||
|
|
5d8b7b4828 | ||
|
|
48e339fd34 | ||
|
|
41cae83741 | ||
|
|
d58f77be10 | ||
|
|
a24f5c12ca | ||
|
|
e73765a5ce | ||
|
|
cd14c5b580 | ||
|
|
9b446bf084 |
30
.github/docker-releases.json
vendored
30
.github/docker-releases.json
vendored
@@ -1,8 +1,32 @@
|
||||
{
|
||||
"latest": {
|
||||
"id": 144022554,
|
||||
"tag_name": "v25.0.4",
|
||||
"html_url": "https://github.com/moby/moby/releases/tag/v25.0.4",
|
||||
"id": 145844215,
|
||||
"tag_name": "v26.0.0",
|
||||
"html_url": "https://github.com/moby/moby/releases/tag/v26.0.0",
|
||||
"assets": []
|
||||
},
|
||||
"v23.0.10": {
|
||||
"id": 147776752,
|
||||
"tag_name": "v23.0.10",
|
||||
"html_url": "https://github.com/moby/moby/releases/tag/v23.0.10",
|
||||
"assets": []
|
||||
},
|
||||
"v26.0.0": {
|
||||
"id": 145844215,
|
||||
"tag_name": "v26.0.0",
|
||||
"html_url": "https://github.com/moby/moby/releases/tag/v26.0.0",
|
||||
"assets": []
|
||||
},
|
||||
"v26.0.0-rc3": {
|
||||
"id": 146688910,
|
||||
"tag_name": "v26.0.0-rc3",
|
||||
"html_url": "https://github.com/moby/moby/releases/tag/v26.0.0-rc3",
|
||||
"assets": []
|
||||
},
|
||||
"v25.0.5": {
|
||||
"id": 147202747,
|
||||
"tag_name": "v25.0.5",
|
||||
"html_url": "https://github.com/moby/moby/releases/tag/v25.0.5",
|
||||
"assets": []
|
||||
},
|
||||
"v26.0.0-rc2": {
|
||||
|
||||
@@ -29,17 +29,20 @@ beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
maybe('parseDefinitions', () => {
|
||||
maybe('getDefinition', () => {
|
||||
// prettier-ignore
|
||||
test.each([
|
||||
[
|
||||
['https://github.com/docker/buildx.git#v0.10.4'],
|
||||
'https://github.com/docker/buildx.git#v0.10.4',
|
||||
['binaries-cross'],
|
||||
path.join(fixturesDir, 'bake-buildx-0.10.4-binaries-cross.json')
|
||||
]
|
||||
])('given %p', async (sources: string[], targets: string[], out: string) => {
|
||||
],
|
||||
])('given %p', async (source: string, targets: string[], out: string) => {
|
||||
const bake = new Bake();
|
||||
const expectedDef = <BakeDefinition>JSON.parse(fs.readFileSync(out, {encoding: 'utf-8'}).trim())
|
||||
expect(await bake.parseDefinitions(sources, targets)).toEqual(expectedDef);
|
||||
expect(await bake.getDefinition({
|
||||
source: source,
|
||||
targets: targets
|
||||
})).toEqual(expectedDef);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,6 +19,8 @@ import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import {Bake} from '../../src/buildx/bake';
|
||||
|
||||
import {ExecOptions} from '@actions/exec';
|
||||
import {BakeDefinition} from '../../src/types/bake';
|
||||
|
||||
const fixturesDir = path.join(__dirname, '..', 'fixtures');
|
||||
@@ -27,31 +29,38 @@ beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('parseDefinitions', () => {
|
||||
describe('getDefinition', () => {
|
||||
// prettier-ignore
|
||||
test.each([
|
||||
[
|
||||
[path.join(fixturesDir, 'bake-01.hcl')],
|
||||
['validate'],
|
||||
[],
|
||||
{silent: true},
|
||||
path.join(fixturesDir, 'bake-01-validate.json')
|
||||
],
|
||||
[
|
||||
[path.join(fixturesDir, 'bake-02.hcl')],
|
||||
['build'],
|
||||
[],
|
||||
undefined,
|
||||
path.join(fixturesDir, 'bake-02-build.json')
|
||||
],
|
||||
[
|
||||
[path.join(fixturesDir, 'bake-01.hcl')],
|
||||
['image'],
|
||||
['*.output=type=docker', '*.platform=linux/amd64'],
|
||||
undefined,
|
||||
path.join(fixturesDir, 'bake-01-overrides.json')
|
||||
]
|
||||
])('given %p', async (sources: string[], targets: string[], overrides: string[], out: string) => {
|
||||
])('given %p', async (files: string[], targets: string[], overrides: string[], execOptions: ExecOptions | undefined, out: string) => {
|
||||
const bake = new Bake();
|
||||
const expectedDef = <BakeDefinition>JSON.parse(fs.readFileSync(out, {encoding: 'utf-8'}).trim())
|
||||
expect(await bake.parseDefinitions(sources, targets, overrides)).toEqual(expectedDef);
|
||||
expect(await bake.getDefinition({
|
||||
files: files,
|
||||
targets: targets,
|
||||
overrides: overrides
|
||||
}, execOptions)).toEqual(expectedDef);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -244,6 +244,41 @@ describe('hasDockerExporter', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasAttestationType', () => {
|
||||
// prettier-ignore
|
||||
test.each([
|
||||
['type=provenance,mode=min', 'provenance', true],
|
||||
['type=sbom,true', 'sbom', true],
|
||||
['type=foo,bar', 'provenance', false],
|
||||
])('given %p for %p returns %p', async (attrs: string, name: string, expected: boolean) => {
|
||||
expect(Inputs.hasAttestationType(name, attrs)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveAttestationAttrs', () => {
|
||||
// prettier-ignore
|
||||
test.each([
|
||||
[
|
||||
'type=provenance,mode=min',
|
||||
'type=provenance,mode=min'
|
||||
],
|
||||
[
|
||||
'type=provenance,true',
|
||||
'type=provenance,disabled=false'
|
||||
],
|
||||
[
|
||||
'type=provenance,false',
|
||||
'type=provenance,disabled=true'
|
||||
],
|
||||
[
|
||||
'',
|
||||
''
|
||||
],
|
||||
])('given %p', async (input: string, expected: string) => {
|
||||
expect(Inputs.resolveAttestationAttrs(input)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('hasGitAuthTokenSecret', () => {
|
||||
// prettier-ignore
|
||||
test.each([
|
||||
|
||||
40
__tests__/cache.test.itg.ts
Normal file
40
__tests__/cache.test.itg.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Copyright 2024 actions-toolkit authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import {describe, expect, it} from '@jest/globals';
|
||||
|
||||
import {Cache} from '../src/cache';
|
||||
|
||||
// prettier-ignore
|
||||
const tmpDir = path.join(process.env.TEMP || '/tmp', 'cache-jest');
|
||||
|
||||
const fixturesDir = path.join(__dirname, 'fixtures');
|
||||
|
||||
describe('cache', () => {
|
||||
it('github-repo', async () => {
|
||||
const r = (Math.random() + 1).toString(36).substring(7);
|
||||
const htcName = `cache-test-github-repo-${r}`;
|
||||
const c = new Cache({
|
||||
htcName: htcName,
|
||||
htcVersion: `v1.0.0+${r}`,
|
||||
baseCacheDir: path.join(tmpDir, '.cache-test'),
|
||||
cacheFile: 'github-repo.json'
|
||||
});
|
||||
expect(await c.save(path.join(fixturesDir, 'github-repo.json'))).not.toEqual('');
|
||||
expect(await c.find()).not.toEqual('');
|
||||
});
|
||||
});
|
||||
@@ -279,6 +279,36 @@ describe('hash', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// https://github.com/golang/go/blob/f6b93a4c358b28b350dd8fe1780c1f78e520c09c/src/strconv/atob_test.go#L36-L58
|
||||
describe('parseBool', () => {
|
||||
[
|
||||
{input: '', expected: false, throwsError: true},
|
||||
{input: 'asdf', expected: false, throwsError: true},
|
||||
{input: '0', expected: false, throwsError: false},
|
||||
{input: 'f', expected: false, throwsError: false},
|
||||
{input: 'F', expected: false, throwsError: false},
|
||||
{input: 'FALSE', expected: false, throwsError: false},
|
||||
{input: 'false', expected: false, throwsError: false},
|
||||
{input: 'False', expected: false, throwsError: false},
|
||||
{input: '1', expected: true, throwsError: false},
|
||||
{input: 't', expected: true, throwsError: false},
|
||||
{input: 'T', expected: true, throwsError: false},
|
||||
{input: 'TRUE', expected: true, throwsError: false},
|
||||
{input: 'true', expected: true, throwsError: false},
|
||||
{input: 'True', expected: true, throwsError: false}
|
||||
].forEach(({input, expected, throwsError}) => {
|
||||
test(`parseBool("${input}")`, () => {
|
||||
if (throwsError) {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(() => Util.parseBool(input)).toThrow();
|
||||
} else {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(Util.parseBool(input)).toBe(expected);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// See: https://github.com/actions/toolkit/blob/a1b068ec31a042ff1e10a522d8fdf0b8869d53ca/packages/core/src/core.ts#L89
|
||||
function getInputName(name: string): string {
|
||||
return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`;
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
ARG DOCKER_VERSION=24.0.5
|
||||
ARG BUILDX_VERSION=0.11.2
|
||||
ARG DOCKER_VERSION=26.0.0
|
||||
ARG BUILDX_VERSION=0.13.1
|
||||
|
||||
FROM node:${NODE_VERSION}-alpine AS base
|
||||
RUN apk add --no-cache cpio findutils git
|
||||
|
||||
@@ -19,12 +19,25 @@ import {Exec} from '../exec';
|
||||
import {Inputs} from './inputs';
|
||||
import {Util} from '../util';
|
||||
|
||||
import {ExecOptions} from '@actions/exec';
|
||||
import {BakeDefinition} from '../types/bake';
|
||||
|
||||
export interface BakeOpts {
|
||||
buildx?: Buildx;
|
||||
}
|
||||
|
||||
export interface BakeCmdOpts {
|
||||
files?: Array<string>;
|
||||
load?: boolean;
|
||||
noCache?: boolean;
|
||||
overrides?: Array<string>;
|
||||
provenance?: string;
|
||||
push?: boolean;
|
||||
sbom?: string;
|
||||
source?: string;
|
||||
targets?: Array<string>;
|
||||
}
|
||||
|
||||
export class Bake {
|
||||
private readonly buildx: Buildx;
|
||||
|
||||
@@ -32,13 +45,17 @@ export class Bake {
|
||||
this.buildx = opts?.buildx || new Buildx();
|
||||
}
|
||||
|
||||
public async parseDefinitions(sources: Array<string>, targets?: Array<string>, overrides?: Array<string>, load?: boolean, push?: boolean, workdir?: string): Promise<BakeDefinition> {
|
||||
public async getDefinition(cmdOpts: BakeCmdOpts, execOptions?: ExecOptions): Promise<BakeDefinition> {
|
||||
execOptions = execOptions || {ignoreReturnCode: true};
|
||||
execOptions.ignoreReturnCode = true;
|
||||
|
||||
const args = ['bake'];
|
||||
|
||||
let remoteDef;
|
||||
let remoteDef: string | undefined;
|
||||
const files: Array<string> = [];
|
||||
const sources = [...(cmdOpts.files || []), cmdOpts.source];
|
||||
if (sources) {
|
||||
for (const source of sources.map(v => v.trim())) {
|
||||
for (const source of sources.map(v => (v ? v.trim() : ''))) {
|
||||
if (source.length == 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -47,7 +64,7 @@ export class Bake {
|
||||
continue;
|
||||
}
|
||||
if (remoteDef) {
|
||||
throw new Error(`Only one remote bake definition is allowed`);
|
||||
throw new Error(`Only one remote bake definition can be defined`);
|
||||
}
|
||||
remoteDef = source;
|
||||
}
|
||||
@@ -58,31 +75,40 @@ export class Bake {
|
||||
for (const file of files) {
|
||||
args.push('--file', file);
|
||||
}
|
||||
if (overrides) {
|
||||
for (const override of overrides) {
|
||||
if (cmdOpts.overrides) {
|
||||
for (const override of cmdOpts.overrides) {
|
||||
args.push('--set', override);
|
||||
}
|
||||
}
|
||||
if (load) {
|
||||
if (cmdOpts.load) {
|
||||
args.push('--load');
|
||||
}
|
||||
if (push) {
|
||||
if (cmdOpts.noCache) {
|
||||
args.push('--no-cache');
|
||||
}
|
||||
if (cmdOpts.provenance) {
|
||||
args.push('--provenance', cmdOpts.provenance);
|
||||
}
|
||||
if (cmdOpts.push) {
|
||||
args.push('--push');
|
||||
}
|
||||
if (cmdOpts.sbom) {
|
||||
args.push('--sbom', cmdOpts.sbom);
|
||||
}
|
||||
|
||||
const printCmd = await this.buildx.getCommand([...args, '--print', ...(targets || [])]);
|
||||
return await Exec.getExecOutput(printCmd.command, printCmd.args, {
|
||||
cwd: workdir,
|
||||
ignoreReturnCode: true,
|
||||
silent: true
|
||||
}).then(res => {
|
||||
const printCmd = await this.buildx.getCommand([...args, '--print', ...(cmdOpts.targets || [])]);
|
||||
return await Exec.getExecOutput(printCmd.command, printCmd.args, execOptions).then(res => {
|
||||
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||
throw new Error(`cannot parse bake definitions: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
|
||||
}
|
||||
return <BakeDefinition>JSON.parse(res.stdout.trim());
|
||||
return Bake.parseDefinition(res.stdout.trim());
|
||||
});
|
||||
}
|
||||
|
||||
public static parseDefinition(dt: string): BakeDefinition {
|
||||
return <BakeDefinition>JSON.parse(dt);
|
||||
}
|
||||
|
||||
public static hasLocalExporter(def: BakeDefinition): boolean {
|
||||
return Inputs.hasExporterType('local', Bake.exporters(def));
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import {parse} from 'csv-parse/sync';
|
||||
|
||||
import {Context} from '../context';
|
||||
import {GitHub} from '../github';
|
||||
import {Util} from '../util';
|
||||
|
||||
const parseKvp = (kvp: string): [string, string] => {
|
||||
const delimiterIndex = kvp.indexOf('=');
|
||||
@@ -176,6 +177,45 @@ export class Inputs {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static hasAttestationType(name: string, attrs: string): boolean {
|
||||
const records = parse(attrs, {
|
||||
delimiter: ',',
|
||||
trim: true,
|
||||
columns: false,
|
||||
relaxColumnCount: true
|
||||
});
|
||||
for (const record of records) {
|
||||
for (const [key, value] of record.map((chunk: string) => chunk.split('=').map(item => item.trim()))) {
|
||||
if (key == 'type' && value == name) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static resolveAttestationAttrs(attrs: string): string {
|
||||
const records = parse(attrs, {
|
||||
delimiter: ',',
|
||||
trim: true,
|
||||
columns: false,
|
||||
relaxColumnCount: true
|
||||
});
|
||||
const res: Array<string> = [];
|
||||
for (const record of records) {
|
||||
for (const attr of record) {
|
||||
try {
|
||||
// https://github.com/docker/buildx/blob/8abef5908705e49f7ba88ef8c957e1127b597a2a/util/buildflags/attests.go#L13-L21
|
||||
const v = Util.parseBool(attr);
|
||||
res.push(`disabled=${!v}`);
|
||||
} catch (err) {
|
||||
res.push(attr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res.join(',');
|
||||
}
|
||||
|
||||
public static hasGitAuthTokenSecret(secrets: string[]): boolean {
|
||||
for (const secret of secrets) {
|
||||
if (secret.startsWith('GIT_AUTH_TOKEN=')) {
|
||||
|
||||
39
src/cache.ts
39
src/cache.ts
@@ -30,6 +30,11 @@ export interface CacheOpts {
|
||||
ghaNoCache?: boolean;
|
||||
}
|
||||
|
||||
export interface CachePostState {
|
||||
dir: string;
|
||||
key: string;
|
||||
}
|
||||
|
||||
export class Cache {
|
||||
private readonly opts: CacheOpts;
|
||||
private readonly ghaCacheKey: string;
|
||||
@@ -37,6 +42,8 @@ export class Cache {
|
||||
private readonly cacheDir: string;
|
||||
private readonly cachePath: string;
|
||||
|
||||
private static readonly POST_CACHE_KEY = 'postCache';
|
||||
|
||||
constructor(opts: CacheOpts) {
|
||||
this.opts = opts;
|
||||
this.ghaCacheKey = util.format('%s-%s-%s', this.opts.htcName, this.opts.htcVersion, this.platform());
|
||||
@@ -56,8 +63,14 @@ export class Cache {
|
||||
core.debug(`Cache.save cached to hosted tool cache ${htcPath}`);
|
||||
|
||||
if (!this.ghaNoCache && cache.isFeatureAvailable()) {
|
||||
core.debug(`Cache.save caching ${this.ghaCacheKey} to GitHub Actions cache`);
|
||||
await cache.saveCache([this.cacheDir], this.ghaCacheKey);
|
||||
core.debug(`Cache.save sending ${this.ghaCacheKey} to post state`);
|
||||
core.saveState(
|
||||
Cache.POST_CACHE_KEY,
|
||||
JSON.stringify({
|
||||
dir: this.cacheDir,
|
||||
key: this.ghaCacheKey
|
||||
} as CachePostState)
|
||||
);
|
||||
}
|
||||
|
||||
return cachePath;
|
||||
@@ -75,7 +88,7 @@ export class Cache {
|
||||
if (await cache.restoreCache([this.cacheDir], this.ghaCacheKey)) {
|
||||
core.info(`Restored ${this.ghaCacheKey} from GitHub Actions cache`);
|
||||
htcPath = await tc.cacheDir(this.cacheDir, this.opts.htcName, this.opts.htcVersion, this.platform());
|
||||
core.info(`Restored to hosted tool cache ${htcPath}`);
|
||||
core.info(`Cached to hosted tool cache ${htcPath}`);
|
||||
return this.copyToCache(`${htcPath}/${this.opts.cacheFile}`);
|
||||
}
|
||||
} else if (this.ghaNoCache) {
|
||||
@@ -87,6 +100,26 @@ export class Cache {
|
||||
return '';
|
||||
}
|
||||
|
||||
public static async post(): Promise<CachePostState | undefined> {
|
||||
const state = core.getState(Cache.POST_CACHE_KEY);
|
||||
if (!state) {
|
||||
core.info(`State not set`);
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
let cacheState: CachePostState;
|
||||
try {
|
||||
cacheState = <CachePostState>JSON.parse(state);
|
||||
} catch (e) {
|
||||
throw new Error(`Failed to parse cache post state: ${e}`);
|
||||
}
|
||||
if (!cacheState.dir || !cacheState.key) {
|
||||
throw new Error(`Invalid cache post state: ${state}`);
|
||||
}
|
||||
core.info(`Caching ${cacheState.key} to GitHub Actions cache`);
|
||||
await cache.saveCache([cacheState.dir], cacheState.key);
|
||||
return cacheState;
|
||||
}
|
||||
|
||||
private copyToCache(file: string): string {
|
||||
core.debug(`Copying ${file} to ${this.cachePath}`);
|
||||
fs.copyFileSync(file, this.cachePath);
|
||||
|
||||
11
src/index.ts
11
src/index.ts
@@ -16,6 +16,8 @@
|
||||
|
||||
import * as core from '@actions/core';
|
||||
|
||||
import {Cache} from './cache';
|
||||
|
||||
const isPost = !!process.env['STATE_isPost'];
|
||||
if (!isPost) {
|
||||
core.saveState('isPost', 'true');
|
||||
@@ -36,7 +38,12 @@ export async function run(main: () => Promise<void>, post?: () => Promise<void>)
|
||||
} catch (e) {
|
||||
core.setFailed(e.message);
|
||||
}
|
||||
} else if (post) {
|
||||
await post();
|
||||
} else {
|
||||
if (post) {
|
||||
await post();
|
||||
}
|
||||
await core.group(`Post cache`, async () => {
|
||||
await Cache.post();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,9 @@ export interface Target {
|
||||
platforms?: Array<string>;
|
||||
pull?: boolean;
|
||||
secret?: Array<string>;
|
||||
'shm-size'?: string;
|
||||
ssh?: Array<string>;
|
||||
tags?: Array<string>;
|
||||
target?: string;
|
||||
ulimits?: Array<string>;
|
||||
}
|
||||
|
||||
22
src/util.ts
22
src/util.ts
@@ -144,4 +144,26 @@ export class Util {
|
||||
public static hash(input: string): string {
|
||||
return crypto.createHash('sha256').update(input).digest('hex');
|
||||
}
|
||||
|
||||
// https://github.com/golang/go/blob/f6b93a4c358b28b350dd8fe1780c1f78e520c09c/src/strconv/atob.go#L7-L18
|
||||
public static parseBool(str: string): boolean {
|
||||
switch (str) {
|
||||
case '1':
|
||||
case 't':
|
||||
case 'T':
|
||||
case 'true':
|
||||
case 'TRUE':
|
||||
case 'True':
|
||||
return true;
|
||||
case '0':
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'false':
|
||||
case 'FALSE':
|
||||
case 'False':
|
||||
return false;
|
||||
default:
|
||||
throw new Error(`parseBool syntax error: ${str}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
yarn.lock
12
yarn.lock
@@ -4465,9 +4465,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"ip@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "ip@npm:2.0.0"
|
||||
checksum: cfcfac6b873b701996d71ec82a7dd27ba92450afdb421e356f44044ed688df04567344c36cbacea7d01b1c39a4c732dc012570ebe9bebfb06f27314bca625349
|
||||
version: 2.0.1
|
||||
resolution: "ip@npm:2.0.1"
|
||||
checksum: d765c9fd212b8a99023a4cde6a558a054c298d640fec1020567494d257afd78ca77e37126b1a3ef0e053646ced79a816bf50621d38d5e768cdde0431fa3b0d35
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -7169,11 +7169,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"undici@npm:^5.25.4":
|
||||
version: 5.27.0
|
||||
resolution: "undici@npm:5.27.0"
|
||||
version: 5.28.3
|
||||
resolution: "undici@npm:5.28.3"
|
||||
dependencies:
|
||||
"@fastify/busboy": ^2.0.0
|
||||
checksum: 3acad25bfe5957aa5edc24eb160b5da7a9c67a5061e2e001929bef4bafed07d93a2accb36d407179c35b3ae56adbe89b49e1dd80d8cea9fdc44dca2037174330
|
||||
checksum: fa1e65aff896c5e2ee23637b632e306f9e3a2b32a3dc0b23ea71e5555ad350bcc25713aea894b3dccc0b7dc2c5e92a5a58435ebc2033b731a5524506f573dfd2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user