Skip to content
28 changes: 1 addition & 27 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ jobs:
- macos-14
- windows-latest
node_version:
- 14
- 16
- 18
- 20
node_arch:
- x64
steps:
Expand All @@ -34,41 +33,16 @@ jobs:
check-latest: true

- name: Setup Python
# NodeJS v14 can use the python included by the CI
if: ${{ matrix.node_version != 14 }}
uses: actions/setup-python@v4
with:
python-version: '3.12'

- name: Setup Python (NodeJS v14)
# While initially tests would pass with no Python setup, additional testing
# is showing issues with CI included Python versions, so we will install our own
if: ${{ matrix.node_version == 14 }}
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install Python Dependencies
# NodeJS v14 doesn't have a newer copy of python, so we don't need to install deps
if: ${{ matrix.node_version != 14 }}
# This is needed for Python 3.12+, since node-gyp requires
# 'distutils', which has been removed
run: python3 -m pip install setuptools

- name: Update node-gyp (NodeJS v14, Windows)
# Need newer node-gyp to support newer Visual Studio versions
if: ${{ matrix.node_version == 14 && runner.os == 'Windows' }}
run: yarn global add node-gyp@9

- name: Install dependencies
if: ${{ matrix.node_version != 14 }}
run: yarn install

- name: Install dependencies (NodeJS v14)
# node-gyp v10+'s `engines` field dropped support for Node 14... Ignore and install anyway.
if: ${{ matrix.node_version == 14 }}
run: yarn install --ignore-engines

- name: Run tests 👩🏾‍💻
run: ./bin/npm test
# Q: Why are we using some random test section when the package.json has a test script?
Expand Down
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
39 changes: 33 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,27 @@ have published it.\
fs.writeFileSync(metadataPath, `${metadataJson}\n`);
}

loadRepository() {
getDefaultBranch(repo) {
try {
// TODO replace with repo.getSymbolicRefTarget once the Windows issues in @pulsar-edit/git-utils have been fixed
// see https://github.com/pulsar-edit/ppm/pull/175/changes/478aa593a92137dd43b9a95842edd1f3a9cfb586
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 +304,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 +414,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 +443,7 @@ have published it.\
}

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