Skip to content
Open
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
23 changes: 23 additions & 0 deletions spec/init-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const path = require('path');
const temp = require('temp');
const CSON = require('season');
const fs = require('../src/fs');
const PackageConverter = require('../src/package-converter');

describe('apm init', () => {
let languagePath, packagePath, themePath;
Expand Down Expand Up @@ -210,3 +211,25 @@ describe('apm init', () => {
});
});
});

describe('PackageConverter.getDownloadUrl', () => {
it('uses HEAD instead of a hardcoded branch name', () => {
const converter = new PackageConverter(
'https://github.com/textmate/r.tmbundle',
'/tmp/fake-dest'
);
expect(converter.getDownloadUrl()).toBe(
'https://github.com/textmate/r.tmbundle/archive/HEAD.tar.gz'
);
});

it('strips trailing .git and slashes before appending archive path', () => {
const converter = new PackageConverter(
'https://github.com/textmate/r.tmbundle.git/',
'/tmp/fake-dest'
);
expect(converter.getDownloadUrl()).toBe(
'https://github.com/textmate/r.tmbundle/archive/HEAD.tar.gz'
);
});
});
85 changes: 85 additions & 0 deletions spec/publish-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,41 @@ describe('apm publish', () => {
expect(callback.calls.mostRecent().args[0]).toBeUndefined();
});

it('publishes successfully with --branch flag', async () => {
const packageToPublish = temp.mkdirSync('apm-test-package-');
const metadata = {
name: 'test',
version: '1.0.0',
"repository": {
"type": "git",
"url": "https://github.com/pulsar-edit/foo"
},
engines: {
atom: '1'
},
dependencies: {
foo: '^5'
},
devDependencies: {
abc: 'git://github.com/user/project.git',
abcd: 'latest',
}
};
fs.writeFileSync(
path.join(packageToPublish, 'package.json'),
JSON.stringify(metadata)
);
process.chdir(packageToPublish);

childProcess.execSync('git init', { cwd: packageToPublish });
childProcess.execSync('git remote add origin https://github.com/pulsar-edit/foo', { cwd: packageToPublish });

const callback = jasmine.createSpy('callback');
await apmRun(['publish', 'patch', '--branch', 'main'], callback);
expect(requests.length).toBe(1);
expect(callback.calls.mostRecent().args[0]).toBeUndefined();
});

it('publishes successfully when the package exists and is being renamed', async () => {
spyOn(Publish.prototype, 'packageExists').and.callFake((name) => {
// If we're renaming the package, we need to ask the API if the package's
Expand Down Expand Up @@ -294,3 +329,53 @@ describe('apm publish', () => {
expect(callback.calls.mostRecent().args[0]).toBeUndefined();
});
});



describe('Publish.getDefaultBranch', () => {
let publish;

beforeEach(() => {
publish = new Publish();
});

it('falls back to main when symbolic-ref is unavailable', () => {
// execSync is bound at import time, so we call getDefaultBranch
// outside a real git repo where symbolic-ref will naturally fail
const repo = {
getConfigValue: jasmine.createSpy('getConfigValue').and.callFake((key) => {
if (key === 'branch.main.remote') return 'origin';
return null;
})
};
expect(publish.getDefaultBranch(repo)).toBe('main');
});

it('falls back to master when main is not configured', () => {
const repo = {
getConfigValue: jasmine.createSpy('getConfigValue').and.callFake((key) => {
if (key === 'branch.master.remote') return 'origin';
return null;
})
};
expect(publish.getDefaultBranch(repo)).toBe('master');
});

it('returns null when no default branch can be determined', () => {
const repo = {
getConfigValue: jasmine.createSpy('getConfigValue').and.returnValue(null)
};
expect(publish.getDefaultBranch(repo)).toBeNull();
});

it('prefers main over master', () => {
const repo = {
getConfigValue: jasmine.createSpy('getConfigValue').and.callFake((key) => {
if (key === 'branch.main.remote') return 'origin';
if (key === 'branch.master.remote') return 'origin';
return null;
})
};
expect(publish.getDefaultBranch(repo)).toBe('main');
});
});
2 changes: 1 addition & 1 deletion src/package-converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class PackageConverter {
getDownloadUrl() {
let downloadUrl = this.sourcePath;
downloadUrl = downloadUrl.replace(/(\.git)?\/*$/, '');
return downloadUrl += '/archive/master.tar.gz';
return downloadUrl += '/archive/HEAD.tar.gz';
}

async downloadBundle() {
Expand Down
37 changes: 31 additions & 6 deletions src/publish.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

const path = require('path');
const { execSync } = require('child_process');

const yargs = require('yargs');
const Git = require('git-utils');
Expand Down Expand Up @@ -29,6 +30,7 @@ class Publish extends Command {
Usage: ppm publish [<newversion> | major | minor | patch | build]
ppm publish --tag <tagname>
ppm publish --rename <new-name>
ppm publish --branch <branch-name>

Publish a new version of the package in the current working directory.

Expand All @@ -50,7 +52,8 @@ have published it.\
);
options.alias('h', 'help').describe('help', 'Print this usage message');
options.alias('t', 'tag').string('tag').describe('tag', 'Specify a tag to publish. Must be of the form vx.y.z');
return options.alias('r', 'rename').string('rename').describe('rename', 'Specify a new name for the package');
options.alias('r', 'rename').string('rename').describe('rename', 'Specify a new name for the package');
return options.alias('b', 'branch').string('branch').describe('branch', 'Specify the default branch of the package repository');
}

// Create a new version and tag use the `npm version` command.
Expand Down Expand Up @@ -272,7 +275,25 @@ have published it.\
fs.writeFileSync(metadataPath, `${metadataJson}\n`);
}

loadRepository() {
getDefaultBranch(repo) {
try {
const ref = execSync('git symbolic-ref refs/remotes/origin/HEAD', {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'ignore']
}).trim();
const match = ref.match(/refs\/remotes\/origin\/(.+)/);
if (match) { return match[1]; }
} catch { /* symbolic-ref may not exist; fall through to heuristic */ }

for (const branch of ['main', 'master']) {
if (repo.getConfigValue(`branch.${branch}.remote`)) {
return branch;
}
}
return null;
}

loadRepository(branch) {
let currentBranch, remoteName, upstreamUrl;
const currentDirectory = process.cwd();

Expand All @@ -281,12 +302,16 @@ have published it.\
throw new Error('Package must be in a Git repository before publishing: https://help.github.com/articles/create-a-repo');
}


currentBranch = repo.getShortHead();
if (currentBranch) {
remoteName = repo.getConfigValue(`branch.${currentBranch}.remote`);
}
if (remoteName == null) { remoteName = repo.getConfigValue('branch.master.remote'); }
if (remoteName == null) {
const defaultBranch = branch || this.getDefaultBranch(repo);
if (defaultBranch && defaultBranch !== currentBranch) {
remoteName = repo.getConfigValue(`branch.${defaultBranch}.remote`);
}
}

if (remoteName) { upstreamUrl = repo.getConfigValue(`remote.${remoteName}.url`); }
if (upstreamUrl == null) { upstreamUrl = repo.getConfigValue('remote.origin.url'); }
Expand Down Expand Up @@ -387,7 +412,7 @@ have published it.\
async run(options) {
let pack, originalName;
options = this.parseOptions(options.commandArgs);
let {tag, rename} = options.argv;
let {tag, rename, branch} = options.argv;
let [version] = options.argv._;

// Normalize variables to ensure they are strings with zero length
Expand Down Expand Up @@ -416,7 +441,7 @@ have published it.\
}

try {
this.loadRepository();
this.loadRepository(branch);
} catch (error) {
return error;
}
Expand Down
Loading