Skip to content

Commit

Permalink
Eliminate remaining uses of YAML except index.yaml
Browse files Browse the repository at this point in the history
Extracted from microsoft#690
  • Loading branch information
BillyONeal committed Sep 3, 2022
1 parent dfb8280 commit decacd0
Show file tree
Hide file tree
Showing 46 changed files with 611 additions and 590 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@ While the usage of `ce` is the same on all platforms, the installation/loading/r
| `artifact metadata` | A description of the locations one or more artifacts describing rules for which ones are deployed given selection of a host architecture, target architecture, or other properties|
| `artifact identity` | A short string that uniquely describes a moniker that a given artifact (and its metadata) can be referenced by. They can have one of the following forms:<br> `full/identity/path` - the full identity of an artifact that is in the built-in artifact source<br>`sourcename:full/identity/path` - the full identity of an artifact that is in the artifact source specified by the sourcename prefix<br>`shortname` - the shortened unique name of an artifact that is in the built-in artifact source<br>`sourcename:shortname` - the shortened unique name of an artifact that is in the artifact source specified by the sourcename prefix<br>Shortened names are generated based off the shortest unique identity path in the given source. |
| `artifact source` | Also known as a “feed”. An Artifact Source is a location that hosts metadata to locate artifacts. (_There is only one source currently_) |
| `project profile` | The per-project configuration file (`environment.yaml` or `environment.json`)
| `AMF`&nbsp;or&nbsp;`Metadata`&nbsp;`Format` | The schema / format of the YAML/JSON files for project profiles, global settings, and artifacts metadata. |
| `activation` | The process by which a particular set of artifacts are acquired and enabled for use in a calling command program.|
| `versions` | Version numbers are specified using the Semver format. If a version for a particular operation isn't specified, a range for the latest version ( `*` ) is assumed. A version or version range can be specified using the npm semver matching syntax. When a version is stored, it can be stored using the version range specified, a space and then the version found. (ie, the first version is what was asked for, the second is what was installed. No need for a separate lock file.) |

Expand Down
15 changes: 0 additions & 15 deletions ce/ce/amf/document-context.ts

This file was deleted.

49 changes: 12 additions & 37 deletions ce/ce/amf/metadata-file.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License.


import { extname } from 'path';
import { Document, isMap, LineCounter, parseDocument, YAMLMap } from 'yaml';
import { Registry } from '../artifacts/registry';
import { i } from '../i18n';
Expand All @@ -13,32 +12,21 @@ import { Session } from '../session';
import { Uri } from '../util/uri';
import { BaseMap } from '../yaml/BaseMap';
import { Options } from '../yaml/Options';
import { toYAML } from '../yaml/yaml';
import { Yaml, YAMLDictionary } from '../yaml/yaml-types';
import { Contacts } from './contact';
import { DemandBlock, Demands } from './demands';
import { DocumentContext } from './document-context';
import { GlobalSettings } from './global-settings';
import { Info } from './info';
import { Registries } from './registries';


export class MetadataFile extends BaseMap implements Profile {
readonly context: DocumentContext;
session!: Session;

private constructor(protected document: Document.Parsed, public readonly filename: string, public lineCounter: LineCounter, public readonly registry: Registry | undefined) {
private constructor(protected document: Document.Parsed, public readonly filename: string, public lineCounter: LineCounter, public readonly file: Uri, public readonly registry: Registry | undefined) {
super(<YAMLMap<string, any>><any>document.contents);
this.context = <DocumentContext>{
filename,
lineCounter,
};
}

async init(session: Session): Promise<MetadataFile> {
this.context.session = session;
this.context.file = session.parseUri(this.context.filename);
this.context.folder = this.context.file.parent;
await this.demandBlock.init(session);
return this;
}
Expand All @@ -53,7 +41,7 @@ export class MetadataFile extends BaseMap implements Profile {
content = '{\n}';
}
const doc = parseDocument(content, { prettyErrors: false, lineCounter: lc, strict: true });
return new MetadataFile(doc, filename, lc, registry).init(session);
return new MetadataFile(doc, filename, lc, session.parseUri(filename), registry).init(session);
}

#info = new Info(undefined, this, 'info');
Expand Down Expand Up @@ -118,32 +106,19 @@ export class MetadataFile extends BaseMap implements Profile {
return this.document.errors.length === 0;
}

get content() {
return toYAML(this.document.toString());
}

async save(uri: Uri = this.context.file): Promise<void> {
// check the filename, and select the format.
let content = '';

switch (extname(uri.path).toLowerCase()) {
case '.yaml':
case '.yml':
// format as yaml
content = this.content;
break;

case '.json':
content = JSON.stringify(this.document.toJSON(), null, 2);
break;
default:
throw new Error(`Unsupported file type ${extname(uri.path)}`);
}
toJsonString() {
let content = JSON.stringify(this.document.toJSON(), null, 2);
if (!content || content === 'null') {
content = '{}\n';
}
await uri.writeUTF8(content);

return content;
}

async save(uri: Uri = this.file): Promise<void> {
await uri.writeUTF8(this.toJsonString());
}

#errors!: Array<string>;
get formatErrors(): Array<string> {
const t = this;
Expand Down Expand Up @@ -288,7 +263,7 @@ export class MetadataFile extends BaseMap implements Profile {

/** @internal */override assert(recreateIfDisposed = false, node = this.node): asserts this is Yaml<YAMLDictionary> & { node: YAMLDictionary } {
if (!isMap(this.node)) {
this.document = parseDocument('{}\n', { prettyErrors: false, lineCounter: this.context.lineCounter, strict: true });
this.document = parseDocument('{}\n', { prettyErrors: false, lineCounter: this.lineCounter, strict: true });
this.node = <YAMLMap<string, any>><any>this.document.contents;
}
}
Expand Down
4 changes: 2 additions & 2 deletions ce/ce/artifacts/artifact.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export class Artifact extends ArtifactBase {
}

get isInstalled() {
return this.targetLocation.exists('artifact.yaml');
return this.targetLocation.exists('artifact.json');
}

get uniqueId() {
Expand Down Expand Up @@ -183,7 +183,7 @@ export class Artifact extends ArtifactBase {

async writeManifest() {
await this.targetLocation.createDirectory();
await this.metadata.save(this.targetLocation.join('artifact.yaml'));
await this.metadata.save(this.targetLocation.join('artifact.json'));
}

async uninstall() {
Expand Down
4 changes: 2 additions & 2 deletions ce/ce/cli/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,12 @@ export async function activateProject(project: ProjectManifest, options?: Activa
if (await activate(artifacts, true, options)) {
trackActivation();
log(blank);
log(i`Project ${projectFile(project.metadata.context.folder)} activated`);
log(i`Project ${projectFile(project.metadata.file.parent)} activated`);
return true;
}

log(blank);
log(i`Failed to activate project ${projectFile(project.metadata.context.folder)}`);
log(i`Failed to activate project ${projectFile(project.metadata.file.parent)}`);

return false;
}
3 changes: 0 additions & 3 deletions ce/ce/interfaces/metadata/metadata-format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,3 @@ import { NugetRegistry } from './registries/nuget-registry';
export type Profile = ProfileBase;

export type RegistryDeclaration = NugetRegistry | LocalRegistry | GitRegistry;

/** values that can be either a single string, or an array of strings */
export type StringOrStrings = string | Array<string>;
4 changes: 2 additions & 2 deletions ce/ce/registries/ArtifactRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { FileType } from '../fs/filesystem';
import { Session } from '../session';
import { Queue } from '../util/promise';
import { Uri } from '../util/uri';
import { isYAML, serialize } from '../yaml/yaml';
import { serialize } from '../yaml/yaml';
import { ArtifactIndex } from './artifact-index';
import { Index } from './indexer';
import { Registries } from './registries';
Expand Down Expand Up @@ -107,7 +107,7 @@ export abstract class ArtifactRegistry implements Registry {
continue;
}

if (type & FileType.File && isYAML(entry.path)) {
if (type & FileType.File && entry.path.endsWith('.json')) {
void q.enqueue(() => processFile(entry));
}
}
Expand Down
6 changes: 2 additions & 4 deletions ce/ce/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ import { isIndexFile, isMetadataFile } from './registries/standard-registry';
import { Channels, Stopwatch } from './util/channels';
import { Queue } from './util/promise';
import { isFilePath, Uri } from './util/uri';
import { isYAML } from './yaml/yaml';


/** The definition for an installer tool function */
type InstallerTool<T extends Installer = any> = (
Expand Down Expand Up @@ -229,7 +227,7 @@ export class Session {
continue;
}

if (type & FileType.File && isYAML(entry.path)) {
if (type & FileType.File && entry.path.endsWith('.json')) {
void q.enqueue(async () => { result = result || await isMetadataFile(entry, s); });
}
}
Expand Down Expand Up @@ -360,7 +358,7 @@ export class Session {
}
for (const [folder, stat] of await this.installFolder.readDirectory(undefined, { recursive: true })) {
try {
const metadata = await MetadataFile.parseMetadata(folder.join('artifact.yaml'), this);
const metadata = await MetadataFile.parseMetadata(folder.join('artifact.json'), this);
result.push({
folder,
id: metadata.id,
Expand Down
24 changes: 4 additions & 20 deletions ce/ce/yaml/yaml.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { Document, Node, Pair, parseDocument, Scalar, stringify, visit, YAMLMap, YAMLSeq } from 'yaml';
import { StringOrStrings } from '../interfaces/metadata/metadata-format';
import { Document, Node, Pair, parseDocument, Scalar, visit, YAMLMap, YAMLSeq } from 'yaml';

/** @internal */
export const createNode = (v: any, b = true) => parseDocument('', { prettyErrors: false }).createNode(v, {});
Expand Down Expand Up @@ -31,6 +30,9 @@ export function getStrings(node: Document.Parsed | YAMLMap, name: string): Array
return [];
}

/** values that can be either a single string, or an array of strings */
export type StringOrStrings = string | Array<string>;

export function setStrings(node: Document.Parsed | YAMLMap, name: string, value: StringOrStrings) {
if (Array.isArray(value)) {
switch (value.length) {
Expand Down Expand Up @@ -58,21 +60,3 @@ export function serialize(value: any) {
});
return document.toString();
}

export function isYAML(path: string) {
path = path.toLowerCase();
return path.endsWith('.yml') || path.endsWith('.yaml') || path.endsWith('.json');
}

export function toYAML(content: string) {
if (content.charAt(0) === '{') {
// the content is in JSON format.
// let's force it to YAML
try {
return stringify(JSON.parse(content)).toString();
} catch (e: any) {
// no worries.
}
}
return content;
}
4 changes: 2 additions & 2 deletions ce/test/core/acquire-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('Acquire', () => {

it('try some downloads', async () => {

const remoteFile = local.session.parseUri('https://raw.githubusercontent.com/microsoft/vscode/main/README.md');
const remoteFile = fs.parse('https://raw.githubusercontent.com/microsoft/vscode/main/README.md');

let acq = acquireArtifactFile(local.session, [remoteFile], 'readme.md', {});

Expand Down Expand Up @@ -42,7 +42,7 @@ describe('Acquire', () => {


it('larger file', async () => {
const remoteFile = local.session.parseUri('https://user-images.githubusercontent.com/1487073/58344409-70473b80-7e0a-11e9-8570-b2efc6f8fa44.png');
const remoteFile = fs.parse('https://user-images.githubusercontent.com/1487073/58344409-70473b80-7e0a-11e9-8570-b2efc6f8fa44.png');

let acq = acquireArtifactFile(local.session, [remoteFile], 'xyz.png', {});

Expand Down
44 changes: 22 additions & 22 deletions ce/test/core/amf-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,42 +19,42 @@ describe('Amf', () => {
after(local.after.bind(local));

it('readProfile', async () => {
const content = await (await readFile(join(rootFolder(), 'resources', 'sample1.yaml'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./sample1.yaml', content, local.session);
const content = await (await readFile(join(rootFolder(), 'resources', 'sample1.json'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./sample1.json', content, local.session);

strict.ok(doc.isFormatValid, 'Ensure it is valid yaml');
strict.ok(doc.isFormatValid, 'Ensure it is valid json');
strict.sequenceEqual(doc.validate(), []);

strict.equal(doc.id, 'sample1', 'name incorrect');
strict.equal(doc.version, '1.2.3', 'version incorrect');
});

it('reads file with nupkg', async () => {
const content = await (await readFile(join(rootFolder(), 'resources', 'repo', 'sdks', 'microsoft', 'windows.yaml'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./windows.yaml', content, local.session);
const content = await (await readFile(join(rootFolder(), 'resources', 'repo', 'sdks', 'microsoft', 'windows.json'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./windows.json', content, local.session);

strict.ok(doc.isFormatValid, 'Ensure it is valid yaml');
strict.ok(doc.isFormatValid, 'Ensure it is valid json');
strict.sequenceEqual(doc.validate(), []);

SuiteLocal.log(doc.content);
SuiteLocal.log(doc.toJsonString());
});

it('load/persist an artifact', async () => {
const content = await (await readFile(join(rootFolder(), 'resources', 'example-artifact.json'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./example-artifact.json', content, local.session);

SuiteLocal.log(doc.content);
SuiteLocal.log(doc.toJsonString());
strict.ok(doc.isFormatValid, 'Ensure it\'s valid');
for (const each of doc.validate()) {
SuiteLocal.log(doc.formatVMessage(each));
}
});

it('profile checks', async () => {
const content = await (await readFile(join(rootFolder(), 'resources', 'sample1.yaml'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./sample1.yaml', content, local.session);
const content = await (await readFile(join(rootFolder(), 'resources', 'sample1.json'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./sample1.json', content, local.session);

strict.ok(doc.isFormatValid, 'Ensure that it is valid yaml');
strict.ok(doc.isFormatValid, 'Ensure that it is valid json');
strict.sequenceEqual(doc.validate(), []);

// fixme: validate inputs again.
Expand Down Expand Up @@ -133,9 +133,9 @@ describe('Amf', () => {
SuiteLocal.log(doc.toString());
});

it('read invalid yaml file', async () => {
const content = await (await readFile(join(rootFolder(), 'resources', 'errors.yaml'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./errors.yaml', content, local.session);
it('read invalid json file', async () => {
const content = await (await readFile(join(rootFolder(), 'resources', 'errors.json'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./errors.json', content, local.session);

strict.equal(doc.isFormatValid, false, 'this document should have errors');
strict.equal(doc.formatErrors.length, 2, 'This document should have two error');
Expand All @@ -144,21 +144,21 @@ describe('Amf', () => {
strict.equal(doc.version, '1.0.2', 'version incorrect');
});

it('read empty yaml file', async () => {
const content = await (await readFile(join(rootFolder(), 'resources', 'empty.yaml'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./empty.yaml', content, local.session);
it('read empty json file', async () => {
const content = await (await readFile(join(rootFolder(), 'resources', 'empty.json'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./empty.json', content, local.session);

strict.ok(doc.isFormatValid, 'Ensure it is valid yaml');
strict.ok(doc.isFormatValid, 'Ensure it is valid json');

const [firstError] = doc.validate();
strict.equal(doc.formatVMessage(firstError), './empty.yaml:1:1 FieldMissing, Missing identity \'id\'', 'Should have an error about id');
strict.equal(doc.formatVMessage(firstError), './empty.json:1:1 FieldMissing, Missing identity \'id\'', 'Should have an error about id');
});

it('validation errors', async () => {
const content = await (await readFile(join(rootFolder(), 'resources', 'validation-errors.yaml'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./validation-errors.yaml', content, local.session);
const content = await (await readFile(join(rootFolder(), 'resources', 'validation-errors.json'))).toString('utf-8');
const doc = await MetadataFile.parseConfiguration('./validation-errors.json', content, local.session);

strict.ok(doc.isFormatValid, 'Ensure it is valid yaml');
strict.ok(doc.isFormatValid, 'Ensure it is valid json');

const validationErrors = Array.from(doc.validate(), (error) => doc.formatVMessage(error));
strict.equal(validationErrors.length, 7, `Expecting 7 errors, found: ${JSON.stringify(validationErrors, null, 2)}`);
Expand Down
2 changes: 1 addition & 1 deletion ce/test/core/repo-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ describe('StandardRegistry Tests', () => {
...t,
};
p.version = rndSemver();
const target = repoFolder.join(`${p.id}-${p.version}.yaml`);
const target = repoFolder.join(`${p.id}-${p.version}.json`);
await target.writeFile(Buffer.from(serialize(p), 'utf8'));
}
}
Expand Down
Loading

0 comments on commit decacd0

Please sign in to comment.